diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed4b640 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +db*/ +.venv/ +venv/ +.idea/ +**/_temp/ +patch_store/ +**/.os +**/*.bin +log*.txt +logs/ +__pycache__/ +poc/ +.env diff --git a/Supervisor.png b/Supervisor.png new file mode 100644 index 0000000..3308c24 Binary files /dev/null and b/Supervisor.png differ diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..b435b96 --- /dev/null +++ b/agent.py @@ -0,0 +1,115 @@ +import dataclasses +from pathlib import Path + +from langchain_core.runnables.graph import NodeStyles, CurveStyle, MermaidDrawMethod +from langchain_openai import AzureChatOpenAI +from langgraph.graph.state import CompiledStateGraph +import abc +from langchain_core.runnables.graph_mermaid import draw_mermaid_png + +from common import LLM + +NEON_THEME = { + "config": { + "theme": "dark", + "themeVariables": { + "background": "#5E5E5E", + "fontFamily": "'Fira Code', monospace", + "primaryColor": "#1f6feb", + "primaryBorderColor": "#3b8eea", + "primaryTextColor": "#f0f6fc", + "lineColor": "#58a6ff", + "nodeBorderRadius": 8, + "edgeLabelBackground": "#00000000", + }, + "flowchart": { + "curve": "basis", + "layout": "elk" + }, + } +} + +NODE_STYLES = NodeStyles( + default="fill:#1f6feb33,stroke:#3b8eea,stroke-width:2px,color:#f0f6fc", + first="fill:#06d6a033,stroke:#06d6a0,stroke-width:2px,color:#f0f6fc", + last="fill:#ff006e33,stroke:#ff006e,stroke-width:2px,color:#f0f6fc" +) + + +def dummy(state): + print(state) + return {} + + +class Agent(abc.ABC): + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + if cls is Agent: + return + + nodes = getattr(cls, "NODES", None) + if nodes is None: + raise TypeError(f"{cls.__name__} must declare a nested `NODES` dataclass") + + if not ( + dataclasses.is_dataclass(nodes) + and nodes.__dataclass_params__.frozen + ): + raise TypeError( + f"{cls.__name__}.NODES must be decorated with @defaultdataclass(frozen=True)" + ) + + wrong = [ + (name, f.type) + for name, f in nodes.__dataclass_fields__.items() # type: ignore[attr-defined] + if f.type is not str + ] + if wrong: + bad = ", ".join(f"{n}: {t!r}" for n, t in wrong) + raise TypeError( + f"{cls.__name__}.NODES fields must be 'str' – offending: {bad}" + ) + + def __init__(self, llm: AzureChatOpenAI = LLM.mini, draw: bool = False): + self._llm = llm + self._graph: CompiledStateGraph = None + self._build() + if draw: + self._draw_graph() + + def get_graph(self): + return self._graph + + def get_llm(self): + return self._llm + + @abc.abstractmethod + def _build(self): + ... + + def _draw_graph(self): + if self._graph: + mermaid_syntax = self._graph.get_graph(xray=True).draw_mermaid( + curve_style=CurveStyle.BASIS, + node_colors=NODE_STYLES, + wrap_label_n_words=4, + frontmatter_config=NEON_THEME, + ) + + draw_mermaid_png( + mermaid_syntax=mermaid_syntax, + output_file_path=f'{self.__class__.__name__}.png', + draw_method=MermaidDrawMethod.API, + background_color="#5E5E5E", + padding=10, + max_retries=1, + retry_delay=1.0, + ) + + # except Exception as e: + # + # pass + + # Definitions of all nodes + + # Definitions of all conditional edges diff --git a/agent_tools/LLM.py b/agent_tools/LLM.py new file mode 100644 index 0000000..7a5af35 --- /dev/null +++ b/agent_tools/LLM.py @@ -0,0 +1,89 @@ +import logging +import os + +from langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddings +from azure.identity import ClientSecretCredential, get_bearer_token_provider, DefaultAzureCredential + +logging.getLogger('azure').setLevel(logging.WARNING) +logging.getLogger('httpx').setLevel(logging.WARNING) + +endpoint = "https://esg-research.openai.azure.com/" + +# https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/ +tenant_id = os.environ.get("AZURE_TENANT_ID") +client_id = os.environ.get("AZURE_CLIENT_ID") +client_secret = os.environ.get("AZURE_CLIENT_SECRET") + +if tenant_id and client_id and client_secret: + credential = ClientSecretCredential( + tenant_id=tenant_id, + client_id=client_id, + client_secret=client_secret, + ) +else: + credential = DefaultAzureCredential( + exclude_environment_credential=True + ) + + +token_provider = get_bearer_token_provider( + credential, + "https://cognitiveservices.azure.com/.default", +) + + +class LLM: + o3 = AzureChatOpenAI( + model="o3", + azure_deployment="o3", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + o4_mini = AzureChatOpenAI( + model="o4-mini", + azure_deployment="o4-mini", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + o3_mini = AzureChatOpenAI( + model="o3-mini", + azure_deployment="o3-mini", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + nano = AzureChatOpenAI( + max_tokens=250, + model="gpt-4.1-nano", + azure_deployment="gpt-4.1-nano", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + temperature=0.0, + streaming=False, + ) + mini = AzureChatOpenAI( + model="gpt-4.1-mini", + azure_deployment="gpt-4.1-mini", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + temperature=1.0, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + embedding = AzureOpenAIEmbeddings( + model="text-embedding-3-small", + azure_deployment="text-embedding-3-small", + api_version="2024-02-01", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider + ) diff --git a/agent_tools/__init__.py b/agent_tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/agent_tools/tools.py b/agent_tools/tools.py new file mode 100644 index 0000000..8849d50 --- /dev/null +++ b/agent_tools/tools.py @@ -0,0 +1,41 @@ +import uuid +from pathlib import Path +from langchain.prompts import ChatPromptTemplate +from langchain_core.documents import Document + +from agent_tools.vector_store import VectorStore +from common import logger, LLM +from patch_analysis.files_collection import file_desc + +DESC_PROMPT = ChatPromptTemplate.from_template( + "Write a maximum of 80 token unformatted paragraph about the Windows executable " + "{filename} package: {package} description: {description}. Include only technical details about its " + "purpose in the system. Keep it short, consistent, and strictly one paragraph. " + "Do not repeat facts. Omit headings, bullets, and conjunctions; the output is " + "for embedding context." +) + + +async def generate_file_info_if_needed(base_path: Path, name: str, package: str): + res = VectorStore.file_info._collection.get( + where={'$and': [{'name': name}, {'package': package}]}, + ) + + if res.get('ids'): + return + + desc = file_desc(base_path) or '' if base_path.exists() else '' + chain = DESC_PROMPT | LLM.nano + result = await chain.ainvoke({"filename": name, 'package': package, "description": desc}) + logger.debug(result.content) + + doc = Document(page_content=result.content, + metadata={'name': name.lower(), 'package': package.lower(), 'description': desc.lower()}) + await VectorStore.file_info.aadd_documents(documents=[doc], ids=[str(uuid.uuid4())]) + +# sample_path = Path( +# r"E:\Git\snippets\patch_wednesday\patch_store\amd64_microsoft-onecore-s..dlers-speechprivacy_31bf3856ad364e35\settingshandlers_speechprivacy.dll\base\settingshandlers_speechprivacy.dll") +# +# print(file_desc(sample_path)) +# +# ts, size = get_pe_ts_size_id(sample_path) diff --git a/agent_tools/vector_store.py b/agent_tools/vector_store.py new file mode 100644 index 0000000..44fd064 --- /dev/null +++ b/agent_tools/vector_store.py @@ -0,0 +1,33 @@ +import os + +os.environ["ANONYMIZED_TELEMETRY"] = "FALSE" +os.environ["CHROMA_TELEMETRY_ENABLED"] = "FALSE" + +from langchain_chroma import Chroma +from common import LLM + + +class VectorStore: + file_info = Chroma( + persist_directory='./db', + collection_name="windows.exe.desc", + embedding_function=LLM.embedding, + collection_metadata={"hnsw:space": "cosine"}, + create_collection_if_not_exists=True, + ) + + func_logic = Chroma( + persist_directory='./db', + collection_name="windows.exe.functions.logic", + embedding_function=LLM.embedding, + collection_metadata={"hnsw:space": "cosine"}, + create_collection_if_not_exists=True, + ) + + reports = Chroma( + persist_directory='./db', + collection_name="windows.exe.rca.reports", + embedding_function=LLM.embedding, + collection_metadata={"hnsw:space": "cosine"}, + create_collection_if_not_exists=True, + ) diff --git a/args.py b/args.py new file mode 100644 index 0000000..b9519c7 --- /dev/null +++ b/args.py @@ -0,0 +1,145 @@ +import argparse +import re + +from agent_tools.vector_store import VectorStore +from common import console, save_to_file +from patch_downloader.filter_by_platform import get_pt_cve_list_by_platform, print_cve_list + + +def cve_type(value: str) -> str: + """Validate CVE pattern CVE-YYYY-NNNNN (4–7 digits).""" + if not re.fullmatch(r"CVE-\d{4}-\d{4,7}", value, flags=re.IGNORECASE): + raise argparse.ArgumentTypeError( + "Invalid CVE format; expected CVE-YYYY-NNNN[…] (e.g. CVE-2025-32713)." + ) + return value.upper() + + +def month_type(value: str) -> str: + """Validate Patch‑Tuesday month pattern YYYY-MMM with English month abbrev.""" + if not re.fullmatch( + r"\d{4}-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)", + value, + flags=re.IGNORECASE, + ): + raise argparse.ArgumentTypeError( + "Invalid month format; expected YYYY-MMM (e.g. 2025-Jul)." + ) + # Normalise capitalisation (2025-Jul → 2025-Jul) + year, mon = value.split("-") + return f"{year}-{mon.capitalize()}" + + +def build_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser( + description="Generate a single CVE report or a Patch Tuesday batch report." + ) + + sub = p.add_subparsers(dest="mode", required=True, metavar="{cve,month,get_cached_report}") + + # get_cached_report MODE + cached_p = sub.add_parser("get_cached_report", help="Print cached report if exist") + cache_mux = cached_p.add_mutually_exclusive_group(required=False) + cache_mux.add_argument( + "--cve", + metavar="CVE-YYYY-NNNNN", + help="CVE identifier (e.g. CVE-2025-32713).", + type=cve_type, + ) + a = cache_mux.add_argument( + "--month", + metavar="YYYY-MMM", + help="Patch Tuesday month (e.g. 2025-Jul).", + type=month_type, + ) + + # cve MODE + cve_p = sub.add_parser("cve", help="Generate report for a single CVE.") + cve_p.add_argument( + "cve_id", + metavar="CVE-YYYY-NNNNN", + help="CVE identifier (e.g. CVE-2025-32713).", + type=cve_type, + ) + + # month MODE + month_p = sub.add_parser("month", help="Generate Patch Tuesday batch report.") + month_p.add_argument( + "month", + metavar="YYYY-MMM", + help="Patch Tuesday month (e.g. 2025-Jul).", + type=month_type, + ) + + flt = month_p.add_mutually_exclusive_group(required=False) + flt.add_argument( + "--platform-name", + dest="platform_name", + metavar="NAME", + help="Substring filter for platform name.", + ) + flt.add_argument( + "--platform-ids", + dest="platform_ids", + metavar="ID1,ID2", + type=lambda s: {x.strip() for x in str(s).split(',') if x.strip()}, + help="Comma-separated list of platform IDs.", + default=set(), + ) + + return p + + +def print_report(cve, to_file: bool = False): + reports = VectorStore.reports.get(where={'cve': cve}) + if reports.get('ids'): + if to_file: + save_to_file(reports) + else: + for r, m in zip(reports.get('documents'), reports.get('metadatas')): + console.info(f'{m}\n{r}') + else: + print('Not found') + + +def get_month_cve(args): + platform_name = args.platform_name if 'platform_name' in args else None + platform_ids = args.platform_ids if 'platform_ids' in args else set() + + if not (platform_name or platform_ids): + if input("Filter by name? [y/N] ").lower().startswith("y"): + platform_name = input("Platform name substring: ").strip() + else: + ids = input("Comma-separated ProductIDs: ").strip() + platform_ids = {s.strip() for s in (ids or "").split(",") if s} + + df, name, ids = get_pt_cve_list_by_platform(month=args.month, targets=platform_ids, + name=platform_name) + console.info(f'List {args.month} CVEs for {name} - {ids}\n\n {print_cve_list(df)}') + cve = df.get_column('CVE').to_list() + + return cve + + +def get_cve_list(argv: list[str]) -> list[str]: + args = build_parser().parse_args(argv) + + if args.mode == "get_cached_report": + if args.cve: + print_report(args.cve) + elif args.month: + cve = get_month_cve(args) + for c in cve: + print_report(c, to_file=True) + + return [] + + if args.mode == "cve": + cve = [args.cve_id] + else: + cve = get_month_cve(args) + + if not input("This operation may take long time. Do you want to continue? [y/N] ").lower().startswith("y"): + return [] + + return cve diff --git a/common.py b/common.py new file mode 100644 index 0000000..943e108 --- /dev/null +++ b/common.py @@ -0,0 +1,422 @@ +import ctypes +import os +import sys +import threading +import uuid +import weakref +from pathlib import Path +import time +from contextlib import ContextDecorator, contextmanager +import polars as pl + +import logging +from logging.config import dictConfig +from collections import deque +from typing import Annotated, Literal, Hashable, Any + +from bindiff import BinDiff +from bindiff.file import FunctionMatch +from langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddings +from azure.identity import ClientSecretCredential, get_bearer_token_provider, DefaultAzureCredential +from defaultdataclass import defaultdataclass, field + +from langgraph.graph import add_messages +from langchain_core.messages import AnyMessage + +logging.getLogger('azure').setLevel(logging.WARNING) +logging.getLogger('httpx').setLevel(logging.WARNING) + +endpoint = "https://esg-research.openai.azure.com/" + +# https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/ +tenant_id = os.environ.get("AZURE_TENANT_ID") +client_id = os.environ.get("AZURE_CLIENT_ID") +client_secret = os.environ.get("AZURE_CLIENT_SECRET") + +if tenant_id and client_id and client_secret: + credential = ClientSecretCredential( + tenant_id=tenant_id, + client_id=client_id, + client_secret=client_secret, + ) +else: + credential = DefaultAzureCredential( + exclude_environment_credential=True + ) + +token_provider = get_bearer_token_provider( + credential, + "https://cognitiveservices.azure.com/.default", +) + +EXECUTABLE_EXTENSIONS = ['.exe', '.dll', '.ocx', '.sys', '.com', '.scr', '.cpl'] + + +@defaultdataclass(frozen=True) +class StateInfo: + messages: Annotated[list[type[AnyMessage]], add_messages] = field(default_factory=list) + # node: deque[str] = field(default_factory=lambda: deque(maxlen=2)) + node: deque[str] = field(default_factory=lambda: deque()) + + +@defaultdataclass +class CveMetadata: + cve: str + title: str + description: str + faq: list + severity: str + impact: str + cvss: dict + cwe: list + publiclyDisclosed: str + exploited: str + products: list + + +@defaultdataclass +class CveDetails: + cve: str + description: str + msrc_report: CveMetadata + + +@defaultdataclass +class Candidates: + query: str + results: list + + +@defaultdataclass +class PatchStoreEntry: + name: str + path: str + kb: str + hash: str + arch: str + package: str + pubkey: str + version: tuple[int, ...] + ms_id: str + uid: str = field(default_factory=lambda: str(uuid.uuid4())) + + # def from_dict(self, data: dict[str, any], overwrite=False): + # for k, v in data.items(): + # if hasattr(self, k): + # if overwrite or getattr(self, k) is None: + # setattr(self, k, v) + # return self + + +# @defaultdataclass +# class Artifact: +# udiff: str +# before_code: str +# after_code: str +# metadata: dict + + +@defaultdataclass +class Artifact: + primary_file: PatchStoreEntry + secondary_file: PatchStoreEntry + changed: list[FunctionMatch] + diff: BinDiff + + +@defaultdataclass +class Report: + cve_details: CveDetails + content: str + confidence: float + artifact: Artifact + + +@defaultdataclass +class Threshold: + candidates: float = 7.5 + '''0.0 - 10.0 indicates the relevancy threshold for the candidate search.''' + security_modification: float = 0.25 + '''0.0 - 1.0 indicates the relevancy threshold for the security modification search.''' + report: float = 0.1 + '''0.0 - 1.0 indicates the confidence threshold for the report accuracy.''' + + +##### + +class LLM: + o3 = AzureChatOpenAI( + model="o3", + azure_deployment="o3", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + o4_mini = AzureChatOpenAI( + model="o4-mini", + azure_deployment="o4-mini", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + o3_mini = AzureChatOpenAI( + model="o3-mini", + azure_deployment="o3-mini", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + nano = AzureChatOpenAI( + max_tokens=250, + model="gpt-4.1-nano", + azure_deployment="gpt-4.1-nano", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + max_retries=4, + temperature=0.0, + streaming=False, + ) + mini = AzureChatOpenAI( + model="gpt-4.1-mini", + azure_deployment="gpt-4.1-mini", + api_version="2024-12-01-preview", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider, + temperature=1.0, + streaming=False, + # model_kwargs={'max_completion_tokens': 100000} + ) + embedding = AzureOpenAIEmbeddings( + model="text-embedding-3-small", + azure_deployment="text-embedding-3-small", + api_version="2024-02-01", + azure_endpoint=endpoint, + azure_ad_token_provider=token_provider + ) + + +#### + + +def get_winsxs(): + if sys.platform != 'win32': + return None + + path_len = ctypes.windll.kernel32.GetWindowsDirectoryW(None, 0) + buffer = ctypes.create_unicode_buffer(path_len) + ctypes.windll.kernel32.GetWindowsDirectoryW(buffer, len(buffer)) + winsxs = Path(buffer.value) / "WinSxS" + + if winsxs.exists(): + return winsxs + return None + + +def get_latest_servicingstack_folder(): + base_path = get_winsxs() + + matching_folders = list(base_path.glob("amd64_microsoft-windows-servicingstack_*")) + + if not matching_folders: + return "No matching folders found." + + try: + latest_folder = max(matching_folders, key=lambda p: p.stat().st_ctime) + return latest_folder + except Exception as e: + raise f"Error determining latest folder: {e}" + + +def retry_on_exception( + _func: callable = None, + max_retries: int = 3, + exceptions: tuple[type[BaseException], ...] | type[BaseException] = BaseException, + delay: float = 0.0 +): + def wrapper(func: callable): + def retry(*args, **kwargs): + attempt = 0 + while True: + try: + return func(*args, **kwargs) + except exceptions as e: + attempt += 1 + if attempt > max_retries: + raise + logger.debug( + "Retrying %s (%d/%d) in %s[s], exception: %s", + func.__name__, attempt, max_retries, delay, e + ) + if delay: + time.sleep(delay) + + return retry + + # Support bare @retry_on_exception and @retry_on_exception(...) + if callable(_func): + return wrapper(_func) + return wrapper + + +class Timer(ContextDecorator): + def __init__(self, name: str = None): + self.name = name + self.start: float = 0.0 + self.elapsed: float = 0.0 + + def __call__(self, func): + self.name = self.name or func.__name__ + return super().__call__(func) + + def __enter__(self): + self.start = time.perf_counter() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.elapsed = time.perf_counter() - self.start + logger.info(f"[{self.name or 'time block'}] elapsed: {self.elapsed:.6f} seconds") + return False + + +def get_patch_store_df(): + cache = Path('db/.patch_store_df') + + if cache.exists(): + return pl.DataFrame.deserialize(cache) + + df = pl.DataFrame([PatchStoreEntry()]) + return df.clear() + + +LOGGING_CONFIG = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'standard': { + 'format': '%(module)s::%(funcName)s|%(asctime)s|%(levelname)s - \t%(message)s' + }, + 'minimal': { + 'format': '%(message)s' + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'standard' + }, + 'minimal_console': { + 'class': 'logging.StreamHandler', + 'formatter': 'minimal' + }, + 'file': { + 'class': 'logging.handlers.RotatingFileHandler', + 'formatter': 'standard', + 'filename': 'log.txt', + 'maxBytes': 10485760, # 10MB + 'backupCount': 5, + 'encoding': 'utf-8', + }, + }, + 'loggers': { + '': { # Root logger + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': True, + }, + } +} + +greeting_msg = ''' + + +#################################################################################################### + +#################################################################################################### + + +''' + + +def create_logger( + name: str, + handlers: list[Literal['file', 'console', 'minimal_console']], + level: str = 'INFO' +): + handlers = handlers or ['console'] + + config = LOGGING_CONFIG.copy() + config['loggers'][name] = { + 'handlers': handlers, + 'level': level, + 'propagate': False, + } + + dictConfig(config) + _logger = logging.getLogger(name=name) + _logger.debug(greeting_msg.replace('<title>', f' Logging {name} started '.center(100, '#'))) + return _logger + + +logger = create_logger('logger', handlers=['file'], level='DEBUG') # , level='DEBUG' +console = create_logger('console', handlers=['file', 'minimal_console']) # , level='DEBUG' + +_lock_table: weakref.WeakValueDictionary[Hashable, threading.Lock] = weakref.WeakValueDictionary() +_table_guard = threading.RLock() + + +def _get_lock(key: Hashable) -> threading.Lock: + with _table_guard: + lock = _lock_table.get(key) + if lock is None: + lock = threading.Lock() + _lock_table[key] = lock + return lock + + +@contextmanager +def resource_lock(key: Hashable): + """ + Usage: + with resource_lock(resource_id): + ... critical section ... + """ + if key is None: + return + + lock = _get_lock(key) + lock.acquire() + try: + yield + finally: + lock.release() + + +def save_to_file(reports: dict[str, Any], path=(Path(__file__).resolve().parent / 'reports')): + for r, m in zip(reports.get('documents'), reports.get('metadatas')): + text = f'{m}\n{r}' + + cve = m.get('cve') + filename = m.get('file') + base_path = path / f"{cve}_{filename}.txt" + + # Handle duplicates by adding an index + if base_path.exists(): + index = 1 + while True: + indexed_path = path / f"{cve}_{filename}_{index}.txt" + if not indexed_path.exists(): + f = indexed_path + break + index += 1 + else: + f = base_path + + f.parent.mkdir(exist_ok=True) + f.write_text(text, encoding="utf-8") diff --git a/defaultdataclass/__init__.py b/defaultdataclass/__init__.py new file mode 100644 index 0000000..5cf4d29 --- /dev/null +++ b/defaultdataclass/__init__.py @@ -0,0 +1,4 @@ +from ._hook_dataclass import defaultdataclass +from dataclasses import field, asdict + +__all__ = ["defaultdataclass", "field", "asdict"] diff --git a/defaultdataclass/_hook_dataclass.py b/defaultdataclass/_hook_dataclass.py new file mode 100644 index 0000000..1fdb263 --- /dev/null +++ b/defaultdataclass/_hook_dataclass.py @@ -0,0 +1,200 @@ +import dataclasses +from types import UnionType +from typing import Any, Optional, TypeVar, Union, get_origin, get_args, dataclass_transform, get_type_hints + +__all__ = ["defaultdataclass"] + +T = TypeVar("T") +_marker = '__is_defaultdataclass__' + + +def _extract_base(typ: type[Any]) -> type[Any]: + origin = get_origin(typ) + + if hasattr(typ, '__metadata__'): + args = get_args(typ) + if args: + return _extract_base(args[0]) + + if origin is Union: + args = tuple(a for a in get_args(typ) if a is not type(None)) + if len(args) == 1: + return _extract_base(args[0]) + return typ + + return typ + + +def _isinstance_generic(value: Any, typ: type[Any]) -> bool: + try: + return isinstance(value, typ) + except TypeError: + origin = get_origin(typ) + return origin is not None and isinstance(value, origin) + + +def _is_defaultdataclass(cls: type) -> bool: + return hasattr(cls, _marker) + + +def _safe_cast(value: Any, target: type[Any]) -> Any: + base_type = _extract_base(target) + + if value is None: + if _is_defaultdataclass(base_type): + return base_type() + else: + return None + + if _isinstance_generic(value, base_type): + return value + + origin = get_origin(base_type) + if origin in (Union, UnionType): + args = get_args(base_type) + for option in args: + try: + return _safe_cast(value, option) # recurse + except (TypeError, ValueError): + continue + raise TypeError( + f"cannot coerce {value!r} to any of " + f"[{' | '.join(getattr(t, '__name__', str(t)) for t in args)}]" + ) + + if target is bytes: + if isinstance(value, str): + return value.encode() + return bytes(value) + if target is bytearray: + if isinstance(value, str): + return bytearray(value, "utf-8") + return bytearray(value) + if target is memoryview: + if isinstance(value, str): + return memoryview(value.encode()) + return memoryview(value) + if target is str and isinstance(value, (bytes, bytearray, memoryview)): + return bytes(value).decode() + + try: + return target(value) + except (ValueError, TypeError) as e: + if _isinstance_generic(value, target): + return value + raise TypeError(f"Cannot assign {value!r} to {getattr(target, '__name__', str(target))}") from e + + +def _collect_annotations_from_mro(cls: type) -> dict[str, Any]: + annotations = {} + + for base in reversed(cls.__mro__): + if hasattr(base, '__annotations__'): + annotations.update(base.__annotations__) + + return annotations + + +@dataclass_transform( + field_specifiers=(dataclasses.field,) +) +def defaultdataclass( + _cls=None, /, *, init=True, repr=True, eq=True, order=False, + unsafe_hash=False, frozen=False, match_args=True, + kw_only=False, slots=False, weakref_slot=False, new_members=False +) -> type[T]: + def wrap(cls: type[T]) -> type[T]: + orig_ann = dict(_collect_annotations_from_mro(cls)) + + if not orig_ann: + # If there are no types to coerce we don't need defaultdataclass + return dataclasses.dataclass( + init=init, + repr=repr, + eq=eq, + order=order, + unsafe_hash=unsafe_hash, + frozen=frozen, + match_args=match_args, + kw_only=kw_only, + slots=slots, + weakref_slot=weakref_slot, + )(cls) + + cls.__annotations__ = {k: Optional[v] for k, v in orig_ann.items()} + + base_map = {n: _extract_base(t) for n, t in orig_ann.items()} + fields = frozenset(base_map) + + def __setattr__(self, name: str, value: Any, _bypass: bool = False) -> None: + if frozen and not _bypass: + raise dataclasses.FrozenInstanceError(f"cannot assign to field {name!r}") + + if name in fields: + value = _safe_cast(value, base_map[name]) + + if value is not None and not _isinstance_generic(value, base_map[name]): + raise TypeError( + f"{name} expects {base_map[name].__name__}, got {type(value).__name__}" + ) + + object.__setattr__(self, name, value) + return + + if new_members: + object.__setattr__(self, name, value) + + def __post_init__(self) -> None: + for name in orig_ann: + if hasattr(self, name): + value = getattr(self, name) + self.__setattr__(name, value, True) + + def from_dict(self, data: dict[str, any], overwrite=False): + for k, v in data.items(): + if hasattr(self, k): + if overwrite or getattr(self, k) is None: + setattr(self, k, v) + return self + + def to_dict(self): + return dataclasses.asdict(self) + + for name in orig_ann: + if not hasattr(cls, name) or name not in cls.__dict__: + setattr(cls, name, dataclasses.field(default=None)) + + if hasattr(cls, '__post_init__'): + original_post_init = cls.__post_init__ + + def chained_post_init(self): + __post_init__(self) + original_post_init(self) + + cls.__post_init__ = chained_post_init + else: + cls.__post_init__ = __post_init__ + + cls.from_dict = from_dict + cls.to_dict = to_dict + + # Apply dataclass decorator + cls = dataclasses.dataclass( + init=init, + repr=repr, + eq=eq, + order=order, + unsafe_hash=unsafe_hash, + frozen=frozen, + match_args=match_args, + kw_only=kw_only, + slots=slots, + weakref_slot=weakref_slot, + )(cls) + + cls.__setattr__ = __setattr__ + cls.__annotations__ = orig_ann + cls.__is_defaultdataclass__ = True + return cls + + return wrap if _cls is None else wrap(_cls) diff --git a/defaultdataclass/_hook_dataclass.pyi b/defaultdataclass/_hook_dataclass.pyi new file mode 100644 index 0000000..54941c0 --- /dev/null +++ b/defaultdataclass/_hook_dataclass.pyi @@ -0,0 +1,13 @@ +# defaultdataclass.pyi +from dataclasses import dataclass as _dc +from typing import Callable, overload, TypeVar +from typing_extensions import dataclass_transform + +T = TypeVar("T") + + +@dataclass_transform(field_specifiers=(_dc.field,)) +@overload +def defaultdataclass(_cls=..., /, *, init=..., repr=..., eq=..., order=..., + unsafe_hash=..., frozen=..., match_args=..., + kw_only=..., slots=..., weakref_slot=..., new_members=...) -> type[T]: ... diff --git a/defaultdataclass/py.typed b/defaultdataclass/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/patch_analysis/__init__.py b/patch_analysis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/patch_analysis/bindiff_analysis.py b/patch_analysis/bindiff_analysis.py new file mode 100644 index 0000000..bdff891 --- /dev/null +++ b/patch_analysis/bindiff_analysis.py @@ -0,0 +1,101 @@ +import asyncio +import difflib +from pathlib import Path + +import binexport +from bindiff import BinDiff +from langchain_core.documents import Document +import polars as pl + +from common import PatchStoreEntry, logger, Timer +from patch_analysis import ida_analysis + + +async def bindiff_files(subjects: pl.DataFrame, curr_kb, prev_kb): + with Timer('analyze and export'): + analysis_tasks: list[ida_analysis.ExecArgs] = [] + for subject in subjects.filter(pl.col('kb') != 'base').iter_rows(named=True): + subject = PatchStoreEntry().from_dict(subject, overwrite=True) + analysis_tasks.append(ida_analysis.ExecArgs(target=Path(subject.path))) + + await ida_analysis.batch_analysis(analysis_tasks, lambda file: not file.target.with_name( + file.target.name + '.BinExport').exists()) + + diffs: list[BinDiff] = [] + + with Timer('generate bindiff'): + for subject in subjects.filter((pl.col('kb') == curr_kb)).iter_rows(named=True): + subject = PatchStoreEntry().from_dict(subject, overwrite=True) + logger.info( + f'Diffing {subject.name} from {curr_kb} against {prev_kb}') # TODO: If there is no prev version use base + + prev_subject = subjects.filter((pl.col('kb') == prev_kb) & + (pl.col('name') == subject.name)).row(0, named=True) + + if not prev_subject: + logger.warning(f'There is no {prev_kb} version available') + continue + + prev_subject = PatchStoreEntry().from_dict(prev_subject, overwrite=True) + + curr_binexport = subject.path + '.BinExport' + prev_binexport = prev_subject.path + '.BinExport' + bindiff_path = f'{subject.path}.{prev_kb}.BinDiff' + + diff = BinDiff.from_binexport_files(curr_binexport, prev_binexport, bindiff_path) + if not diff: + logger.warning(f'Faild to bindiff {subject.name}') + continue + + diffs.append(diff) + + return diffs + + +def create_diff_text(before: Path, after: Path): + if not (before.exists() and after.exists()): + return None, None, None + + before_code = before.read_text(encoding="utf-8") + after_code = after.read_text(encoding="utf-8") + + udiff = difflib.unified_diff( + before_code.splitlines(), + after_code.splitlines(), + fromfile=f'{before.name} before', + tofile=f'{after.name} after', + lineterm="" + ) + + return "\n".join(udiff), before_code, after_code + + +@Timer() +async def analyze_diff(diff: BinDiff): + decompile = Path('patch_analysis/idapython/decompile.py') + + changed = [v for k, v in diff.primary_functions_match.items() if v.similarity < 1.0] + primary_funcs = {f'{f.address1:X}' for f in changed} - {i.stem for i in + (diff.primary.path.parent / '__funcs__').glob('*.c')} + secondary_funcs = {f'{f.address2:X}' for f in changed} - {i.stem for i in + (diff.secondary.path.parent / '__funcs__').glob('*.c')} + + analysis_tasks: list[ida_analysis.ExecArgs] = [] + + def add_to_tasks(export: binexport.program.ProgramBinExport, functions: list[str]): + idb = export.path.with_suffix('.i64') + if idb.exists(): + N: int = 500 + for i in range(0, len(functions), N): + analysis_tasks.append(ida_analysis.ExecArgs(target=idb, + script=decompile, + args=[f for f in functions[i:i + N]], + log=f'logs/{idb.name}.log' + )) + + add_to_tasks(diff.primary, list(primary_funcs)) + add_to_tasks(diff.secondary, list(secondary_funcs)) + + await ida_analysis.batch_analysis(analysis_tasks) + + return sorted(changed, key=lambda x: (x.similarity, -x.confidence)) diff --git a/patch_analysis/files_collection.py b/patch_analysis/files_collection.py new file mode 100644 index 0000000..23e10bb --- /dev/null +++ b/patch_analysis/files_collection.py @@ -0,0 +1,237 @@ +import hashlib +import re +import uuid +from pathlib import Path +from typing import Generator + +import pefile + +from common import get_winsxs, logger, EXECUTABLE_EXTENSIONS, resource_lock, console +import polars as pl +from dataclasses import dataclass, field + + +@dataclass +class UpdatesInfo: + os_name: str = None + os_id: str = None + prev_kb: str = None + curr_kb: str = None + prev_extracted: Path = None + curr_extracted: Path = None + prev_df: pl.dataframe.DataFrame = None + curr_df: pl.dataframe.DataFrame = None + relevant_df: pl.dataframe.DataFrame = None + relevant_r_patch_df: pl.dataframe.DataFrame = None + + +@dataclass +class Metadata: + path: str + name: str + hash: str + kb: str + delta_type: str + arch: str + package: str + pubkey: str + version: tuple[int, ...] + lang: str + checksum: str + + +component_pattern = re.compile( + r"^(?P<arch>[^_]+)_" # Architecture + r"(?P<package>[^_]+(?:[._-][^_]+)*)_" # Package name with potential dots/hyphens + r"(?P<pubkey>[0-9a-f]{16})_" # Public key (16 hex chars) + r"(?P<version>[\d.]+)_" # Version numbers + r"(?P<lang>[^_]+)_" # Language tag + r"(?P<checksum>[0-9a-f]+)$" # Component hash +) + + +def get_file_hash(file: Path | bytes) -> str: + """Calculate SHA256 hash of a file.""" + sha256_hash = hashlib.sha256() + if isinstance(file, Path): + with open(file, "rb") as f: + # Read and update hash in chunks to handle large files efficiently + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + elif isinstance(file, bytes): + sha256_hash.update(file) + else: + raise TypeError('The provided file is not supported') + + return sha256_hash.hexdigest() + + +def version_tuple(ver: str) -> tuple[int, ...]: + return tuple(int(part) for part in ver.split('.')) + + +def get_files(kb: str, paths: list[Path] | Generator, collect_hash) -> list[Metadata]: + data: list[Metadata] = [] + for p in paths: + if p.is_file(): + component = None + for part in reversed(p.parts[:-1]): + component = component_pattern.match(part) + if component: + break + + if component is None: + continue + + delta_type = p.parts[-2] + gd = component.groupdict() + gd["version"] = version_tuple(gd["version"]) + + try: + data.append(Metadata( + path=str(p.absolute()), + name=p.name.lower(), + kb=kb, + hash=get_file_hash(p) if collect_hash else None, + delta_type=delta_type if delta_type in ['r', 'f', 'n'] else None, + **gd + )) + except PermissionError as e: + print(e) + + return data + + +def generate_df(kb: str, paths: list[Path] | Path, collect_hash) -> pl.DataFrame: + if isinstance(paths, Path): + if not paths.is_dir(): + logger.error('The path provided is not a directory') + return + else: + paths = paths.rglob("*") + + files = get_files(kb, paths, collect_hash) + df = pl.DataFrame(files) + return df + + +# TODO: Remove and make it better +def get_update_dataframe(kb: str, paths: list[Path] | Path, collect_hash=True, cache: Path | None = None): + console.info(f'[*] Indexing {kb} delta files') + if cache: + with resource_lock(cache.resolve()): + if cache and cache.exists(): + return pl.DataFrame.deserialize(cache) + + df = generate_df(kb, paths, collect_hash) + if cache: + df.serialize(cache, format="binary") + else: + df = generate_df(kb, paths, collect_hash) + + return df + + +def get_winsxs_df(base: str): + # TODO: support more OS versions using installation ISOs + return get_update_dataframe('winsxs', get_winsxs(), collect_hash=False, + cache=Path('db/winsxs.bin')) + + +def get_report(report, arch): + return [(report.parent / p) for p in report.read_text().splitlines() if arch in p] + + +filter_executables = pl.any_horizontal( + [pl.col("name") + .str.to_lowercase() + .str.ends_with(ext) + for ext in EXECUTABLE_EXTENSIONS] +) + + +def correlate_updates(info: UpdatesInfo): + winsxs_df = get_winsxs_df(info.os_name) + + prev_report = get_report(info.prev_extracted / 'report.txt') + curr_report = get_report(info.curr_extracted / 'report.txt') + + prev_df = get_update_dataframe(info.prev_kb, prev_report) + curr_df = get_update_dataframe(info.curr_kb, curr_report) + + winsxs_df = winsxs_df.filter(filter_executables) + r_patch = winsxs_df.filter(pl.col("arch").eq("amd64") & pl.col("delta_type").eq("r")) + + # All the files in the current KB that was changed from the last KB + # and have a reverse patch in the WinSxS folder + relevant_df = ( + curr_df.filter( + pl.col("arch").eq("amd64") # filter the x64 only and unmodified files + & ~pl.col("hash").is_in(prev_df["hash"]) + ).join(r_patch.select("package", "pubkey", "arch").unique(), # Correlate with the winsxs folder + on=["package", "pubkey", "arch"], + how="semi", + ) + ) + + relevant_r_patch_df = r_patch.join( + relevant_df.select(["package", "pubkey", "arch"]).unique(), + on=["package", "pubkey", "arch"], + how="semi", + ) + + # Filter files names as well + relevant_df = relevant_df.filter( + pl.col('name').str.to_lowercase().is_in( + relevant_r_patch_df.get_column('name').str.to_lowercase())) + + return prev_df, curr_df, relevant_df, relevant_r_patch_df + + +def file_desc(file: Path) -> str | None: + try: + pe = pefile.PE(file, fast_load=True) + pe.parse_data_directories( + [pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']] + ) + + for fi in pe.FileInfo: + for v in fi: + if v.name == 'StringFileInfo': + for st in getattr(v, 'StringTable', []): + return st.entries.get(b'FileDescription').decode(errors='replace') + + return None + except (AttributeError, pefile.PEFormatError): + return None + + +def get_pe_ts_size_id(file: Path | bytes): + if isinstance(file, Path): + pe = pefile.PE(name=file, fast_load=True) + elif isinstance(file, bytes): + pe = pefile.PE(data=file, fast_load=True) + else: + raise TypeError(f'{type(file)} is not supported') + + ts_raw = pe.FILE_HEADER.TimeDateStamp + img_size = pe.OPTIONAL_HEADER.SizeOfImage + + return ts_raw, img_size + + +def get_pe_ms_id(file: Path | bytes): + ts_raw, img_size = get_pe_ts_size_id(file) + ms_id = f'{ts_raw:08X}{img_size:X}' + return ms_id + + # logger.debug(f'File id is {ms_id}\n' + # f'https://msdl.microsoft.com/download/symbols/{file.name}/{ms_id}/{file.name}') + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/patch_analysis/ida_analysis.py b/patch_analysis/ida_analysis.py new file mode 100644 index 0000000..1d5d732 --- /dev/null +++ b/patch_analysis/ida_analysis.py @@ -0,0 +1,131 @@ +import argparse +import asyncio +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Callable + +from common import logger + + +@dataclass +class ExecArgs: + target: Path + log: str = None + script: Path = Path("patch_analysis/idapython/analyze.py") + args: list = None + ida_path: Path = Path(r"C:\Program Files\IDA Pro 8.0\idat64.exe") + + +def is_valid_args(args: ExecArgs): + if not args.ida_path.is_file(): + logger.error(f"ERROR: IDA executable not found at {args.ida_path!r}") + return False + if not args.script.is_file(): + logger.error(f"ERROR: IDAPython script not found at {args.script!r}") + return False + if not args.target.exists(): + logger.error(f"ERROR: Target file not found at {args.target!r}") + return False + + return True + + +def analyze_executable(args: ExecArgs | argparse.Namespace): + if not is_valid_args(args): + return -1, None, None + + cmd = [ + f'"{str(args.ida_path)}"', + f'-L"{args.log}"' if args.log else '', + "-A", + f'-S"{args.script}"', + f'"{str(args.target)}"' + ] + + try: + result = subprocess.run( + " ".join(cmd), + shell=True, + check=False, + universal_newlines=True + ) + except Exception as e: + print(f"Failed to launch IDA: {e}", file=sys.stderr) + return -1 + + return result.returncode + + +async def aanalyze_executable(args: ExecArgs): + if not is_valid_args(args): + return -1, None, None + + cmd = f'"{args.ida_path}"' + if args.log: + cmd += f' -L"{args.log}"' + + cmd += ' -A' + + script = [args.script] + if args.args: + script.extend(args.args) + + esc_script = subprocess.list2cmdline([subprocess.list2cmdline(script)]) + cmd += f" -S{esc_script}" + + cmd += f' "{args.target}"' + + process = await asyncio.create_subprocess_shell(cmd) + await process.wait() + return process.returncode + + +async def batch_analysis(files: list[ExecArgs], condition: Callable[[ExecArgs], bool] = lambda _: True): + while files: + current: list[ExecArgs] = [] + remains: list[ExecArgs] = [] + for file in files: + if not any(x for x in current if x.target == file.target): + current.append(file) + else: + remains.append(file) + + tasks = [aanalyze_executable(file) for file in current if condition(file)] + + await asyncio.gather(*tasks) + files = remains + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Run IDA Pro headless analysis on a target file." + ) + parser.add_argument( + "--ida-path", + type=Path, + default=Path(r"C:\Program Files\IDA Pro 8.0\idat64.exe"), + help="Full path to idat64.exe" + ) + parser.add_argument( + "--log", + type=str, + default="log.txt", + help="Log file path (relative or absolute)" + ) + parser.add_argument( + "--script", + type=Path, + default=Path("idapython/main.py"), + help="Path to your IDAPython script" + ) + parser.add_argument( + "target", + type=Path, + help="The binary or SYS file to analyze" + ) + + args = parser.parse_args() + + analyze_executable(args) diff --git a/patch_analysis/idapython/analyze.py b/patch_analysis/idapython/analyze.py new file mode 100644 index 0000000..1892d04 --- /dev/null +++ b/patch_analysis/idapython/analyze.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +import idaapi +import ida_auto +import ida_pro +import idc +from pathlib import Path + + +def main(): + # ida_auto.auto_mark_range(0, idaapi.BADADDR, ida_auto.AU_FINAL) + ida_auto.auto_wait() + + idaapi.flush_buffers() + + idb_path = Path(idc.get_input_file_path()) + output_file = str(idb_path.resolve()) + + # idc.gen_file(idc.OFILE_ASM, output_file + ".asm", 0, idaapi.BADADDR, 0) + # idc.gen_file(idc.OFILE_MAP, output_file + ".map", 0, idaapi.BADADDR, 0) + # idc.gen_file(idc.OFILE_LST, output_file + ".lst", 0, idaapi.BADADDR, 0) + # idc.gen_file(idc.OFILE_IDC, output_file + ".idc", 0, idaapi.BADADDR, idc.GENFLG_MAPDMNG) + # + export_path = (output_file + ".BinExport").replace('\\', '/') + # print(export_path) + idaapi.ida_expr.eval_idc_expr(None, idaapi.BADADDR, 'BinExportBinary("' + export_path + '");') + + ida_pro.qexit(0) + + +if __name__ == "__main__": + main() diff --git a/patch_analysis/idapython/decompile.py b/patch_analysis/idapython/decompile.py new file mode 100644 index 0000000..da5ad95 --- /dev/null +++ b/patch_analysis/idapython/decompile.py @@ -0,0 +1,106 @@ +import argparse +from pathlib import Path + +import ida_auto +import ida_hexrays +import ida_ida +import ida_idp +import ida_loader +import idaapi +import idc + + +def parse_args() -> list[int]: + parser = argparse.ArgumentParser( + prog="batch_decompile_funcs.py", + description="Decompile the specified list of functions and " + "save each one to __funcs__/<ea>.c next to the input file", + ) + parser.add_argument( + "func_list", + nargs="+", + type=lambda x: int(x, 16), + metavar="ea", + help="Function start address (decimal -or- 0x… hex)", + ) + try: + ns = parser.parse_args(idc.ARGV[1:]) + except SystemExit as e: + raise e + + return ns.func_list + + +_DECOMPILERS: dict[int, str] = { + ida_idp.PLFM_386: "hexrays", + ida_idp.PLFM_ARM: "hexarm", + ida_idp.PLFM_PPC: "hexppc", + ida_idp.PLFM_MIPS: "hexmips", +} + + +def init_hexrays(): + cpu = ida_idp.ph.id + plugin = _DECOMPILERS.get(cpu) + if plugin is None: + print(f"[!] Unsupported processor family id={cpu}") + return False + + # 64-bit variants use different DLL names for x86, or “…64” suffix for others + if ida_ida.inf_is_64bit(): + plugin = "hexx64" if cpu == ida_idp.PLFM_386 else f"{plugin}64" + + if not ida_loader.load_plugin(plugin): + print(f"[!] Failed to load decompiler plug-in '{plugin}'") + return False + + if not ida_hexrays.init_hexrays_plugin(): + print(f"[!] Hex-Rays initialisation failed for '{plugin}'") + return False + + return True + + +def decompile_to_file(ea: int, out_dir: Path): + file_path = out_dir / f"{ea:X}.c" + print(f"[*] {ea:x}: decompiling...") + + try: + cfunc = ida_hexrays.decompile(ea) + if cfunc is None: + raise RuntimeError("Hex-Rays returned None") + text = str(cfunc) + "\n" + except Exception as exc: + print(f"Decompilation failure at {ea:#x}: " + f"{exc.__class__.__name__}: {exc} */\n") + return + + file_path.write_text(text, encoding="utf-8") + + +def main(): + e_code = 0 + try: + ida_auto.auto_wait() + func_eas = parse_args() + + if not init_hexrays(): + raise RuntimeError('Decompiler initialization failed') + + input_path = Path(idc.get_input_file_path()).resolve() + out_dir = input_path.parent / "__funcs__" + out_dir.mkdir(exist_ok=True) + + for ea in func_eas: + decompile_to_file(ea, out_dir) + + print("[+] All requested functions processed. Output in", out_dir) + except ... as e: + e_code = 1 + raise e + finally: + idaapi.qexit(e_code) + + +if __name__ == "__main__": + main() diff --git a/patch_analysis/patch_delta.py b/patch_analysis/patch_delta.py new file mode 100644 index 0000000..750c6c2 --- /dev/null +++ b/patch_analysis/patch_delta.py @@ -0,0 +1,132 @@ +from pathlib import Path + +from patch_analysis.files_collection import get_file_hash, get_pe_ms_id, version_tuple +from patch_extractor import patch_tools +from common import logger, PatchStoreEntry, console +import polars as pl + +pt = patch_tools.PatchTools() + + +def recursive_patch(base, patch): + try: + while pt.is_patch(patch): + try: + patch = pt.apply(memoryview(base), memoryview(patch)) + except RuntimeError: + patch = pt.apply(None, memoryview(patch)) + except RuntimeError: + return None + + return patch + + +def patch_entry(entry: dict, + base_kb: str, + curr_kb: str, + prev_kb: str, + prev_df: pl.DataFrame, + filtered_base_df: pl.DataFrame + ) -> tuple[PatchStoreEntry | None, PatchStoreEntry | None, PatchStoreEntry | None]: + collect_files: dict[str, Path] = {} + entry_df = pl.DataFrame([entry]) + + base_path = Path('db') / 'patch_store' / f"{entry['arch']}_{entry['package']}_{entry['pubkey']}" / entry['name'] + + base_kb_path = base_path / base_kb + + base_entry = None + curr_entry = None + prev_entry = None + + if not (base_kb_path / entry['name']).exists(): + winsxs_patch = filtered_base_df.join( + entry_df, + on=['name', 'package', 'arch', 'pubkey'], + how='semi') + + if winsxs_patch.is_empty(): + return base_entry, curr_entry, prev_entry + + collect_files['reverse_delta'] = Path(winsxs_patch.item(0, column="path")) + collect_files['current'] = collect_files['reverse_delta'].parents[1] / winsxs_patch.item(0, column="name") + + current = collect_files['current'].open('rb').read() + reverse = collect_files['reverse_delta'].open('rb').read() + + base = recursive_patch(current, reverse) + + if base is None: + console.error(f"[-] Patching base {entry['name']} failed") + return base_entry, curr_entry, prev_entry + + base_kb_path.mkdir(parents=True, exist_ok=True) + + (base_kb_path / entry['name']).write_bytes(base) + + console.debug(f'[+] {entry["name"]} base is patched') + + base_entry = PatchStoreEntry( + path=str(base_kb_path / entry['name']), + kb=base_kb, + hash=get_file_hash(base), + ms_id=get_pe_ms_id(base), + version=version_tuple(base_kb) + ) + base_entry.from_dict(winsxs_patch.row(0, named=True)) + else: + # logger.debug(f'[+] {base_path} is already exist, skip patch') + base = (base_kb_path / entry['name']).read_bytes() + + if not (base_path / curr_kb / entry['name']).exists(): + collect_files['new_delta'] = Path(entry['path']) + + np = pt.map_file(collect_files['new_delta']) + np = recursive_patch(base, np) + + if np: + (base_path / curr_kb).mkdir(parents=True, exist_ok=True) + (base_path / curr_kb / entry['name']).write_bytes(np) + + console.debug(f'[+] {curr_kb} {entry["name"]} is patched') + curr_entry = PatchStoreEntry( + path=str(base_path / curr_kb / entry['name']), + kb=curr_kb, + hash=get_file_hash(np), + ms_id=get_pe_ms_id(np) + ) + curr_entry.from_dict(entry) + else: + console.error(f"[-] Patching {curr_kb} {entry['name']} failed") + + if not (base_path / prev_kb / entry['name']).exists(): + old_patch = prev_df.join( + entry_df, + on=['name', 'package', 'arch', 'pubkey'], + how='semi') + + if old_patch.is_empty(): + logger.debug(f'Cannot find near patch for {entry["name"]}, fallback to base') + else: + collect_files['previous_delta'] = Path(old_patch.item(0, column="path")) + + op = pt.map_file(collect_files['previous_delta']) + op = recursive_patch(base, op) + + if op: + (base_path / prev_kb).mkdir(parents=True, exist_ok=True) + (base_path / prev_kb / entry['name']).write_bytes(op) + + console.debug(f'[+] {prev_kb} {entry["name"]} is patched') + + prev_entry = PatchStoreEntry( + path=str(base_path / prev_kb / entry['name']), + kb=prev_kb, + hash=get_file_hash(op), + ms_id=get_pe_ms_id(op) + ) + prev_entry.from_dict(old_patch.row(0, named=True)) + else: + console.debug(f"[-] Patching {prev_kb} {entry['name']} failed") + + return base_entry, curr_entry, prev_entry diff --git a/patch_downloader/__init__.py b/patch_downloader/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/patch_downloader/cve_enrichment.py b/patch_downloader/cve_enrichment.py new file mode 100644 index 0000000..f682fb0 --- /dev/null +++ b/patch_downloader/cve_enrichment.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +import argparse +import html +import json +import re + +import requests + +from common import CveMetadata + +BASE_VULN = "https://api.msrc.microsoft.com/sug/v2.0/sugodata/v2.0/en-US" +BASE_AFFECT = "https://api.msrc.microsoft.com/sug/v2.0/en-US" +HDRS = {"Accept": "application/json"} + + +def get_vuln_record(cve: str) -> dict: + url = f"{BASE_VULN}/vulnerability?$filter=cveNumber in ('{cve}')" + resp = requests.get(url, headers=HDRS, timeout=30) + resp.raise_for_status() + items = resp.json().get("value", []) + if not items: + raise RuntimeError(f"{cve} not found in SUG.") + return items[0] + + +def get_affected_products(cve: str) -> list[dict]: + url = f"{BASE_AFFECT}/affectedProduct?$filter=cveNumber in ('{cve}')" + resp = requests.get(url, headers=HDRS, timeout=30) + resp.raise_for_status() + return resp.json().get("value", []) + + +def report(cve: str) -> CveMetadata: + vuln = get_vuln_record(cve) + products = get_affected_products(cve) + + def strip_html(s): + re.sub(r"<[^>]+>", "", html.unescape(s)).replace("\\n", "\n").strip() + + out = CveMetadata( + cve=vuln.get("cveNumber", cve), + title=vuln.get("cveTitle", ""), + description=vuln.get("unformattedDescription", ""), + faq=[strip_html(a["description"]) for a in vuln.get("articles") if a.get("articleType") == "FAQ"], + severity=vuln.get("severity"), + impact=vuln.get("impact"), + cvss=dict( + baseScore=float(vuln.get("baseScore") or 0.0), + vectorString=vuln.get("vectorString", ""), + ), + cwe=vuln.get("cweList", []), + publiclyDisclosed=vuln.get("publiclyDisclosed", False), + exploited=vuln.get("exploited", False), + products=[], + ) + + seen: set[int] = set() + + for p in products: + pid = p.get("productId") + kb_list = p.get("kbArticles") or [] + articles = [] + for kb in kb_list: + articles.append(dict( + article=kb.get("articleName"), + supercedence=kb.get("supercedence"), + type=kb.get("downloadName").lower(), + fixedBuild=kb.get("fixedBuildNumber") + )) + key = pid + if key in seen: + continue + seen.add(key) + + out.products.append( + dict( + product=p.get("product"), + productId=pid, + architecture=p.get("architecture"), + baseVersion=p.get("baseProductVersion"), + articles=articles, + # initialReleaseDate=p.get("initialReleaseDate"), + ) + ) + + return out + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("cve", help="CVE-ID (e.g. CVE-2025-27480)") + args = parser.parse_args() + print(json.dumps(report(args.cve), indent=2)) + + +if __name__ == "__main__": + main() diff --git a/patch_downloader/filter_by_platform.py b/patch_downloader/filter_by_platform.py new file mode 100644 index 0000000..b7169f8 --- /dev/null +++ b/patch_downloader/filter_by_platform.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +import argparse, json, re, sys +import textwrap +from pathlib import Path +from typing import Iterable, Sequence + +import polars as pl +import requests + +from common import logger + +TOKEN = re.compile(r"[^a-z0-9]+") + + +def words(text: str) -> list[str]: + return TOKEN.sub(" ", text.lower()).split() + + +def download(month: str) -> dict: + res = requests.get( + f"https://api.msrc.microsoft.com/cvrf/{month}", + headers={"Accept": "application/json"}, + timeout=30, + ) + if res.status_code != 200: + sys.exit(f"HTTP {res.status_code}: {res.text[:120]}") + try: + return res.json() + except json.JSONDecodeError: + sys.exit("Server did not return JSON") + + +def product_pool(cvrf: dict) -> Sequence[dict]: + return cvrf["ProductTree"]["FullProductName"] + + +def full_token_matches( + needle_tokens: set[str], candidates: Iterable[dict] +) -> list[dict]: + hits = [] + for p in candidates: + if needle_tokens.issubset(words(p["Value"])): + hits.append(p) + return hits + + +def interactive_pick(options: Sequence[str]) -> str: + print("Possible matches:") + for i, name in enumerate(options, 1): + print(f"{i}) {name}") + sel = input(f"Choose 1-{len(options)} [1]: ").strip() + try: + idx = int(sel) - 1 + except ValueError: + idx = 0 + if not 0 <= idx < len(options): + idx = 0 + return options[idx] + + +def pick_ids( + cvrf: dict, + query: str | None, + ids: set[str], +) -> tuple[set[str], list[str]]: + name_by_id = { + p["ProductID"]: p["Value"] + for p in cvrf["ProductTree"]["FullProductName"] + } + chosen_names: list[str] = [name_by_id[i] for i in ids if i in name_by_id] + + if query: + tokens = set(words(query)) + pool = product_pool(cvrf) + hits = full_token_matches(tokens, pool) + + if not hits: + scored = sorted( + pool, + key=lambda p: len(tokens & set(words(p["Value"]))), + reverse=True, + )[:10] + chosen = interactive_pick([p["Value"] for p in scored]) + ids.update(p["ProductID"] for p in scored if p["Value"] == chosen) + chosen_names.append(chosen) + else: + names = sorted({h["Value"] for h in hits}) + chosen = names[0] if len(names) == 1 else interactive_pick(names) + ids.update(p["ProductID"] for p in hits if p["Value"] == chosen) + chosen_names.append(chosen) + + print(f"Selected product id: {ids}") + return ids, chosen_names + + +def collect_cves(cvrf: dict, wanted: set[str]) -> list[dict]: + rows = [] + for v in cvrf["Vulnerability"]: + affected = set() + for st in v.get("ProductStatuses", []): + pids = st.get("ProductID", []) + if isinstance(pids, str): + pids = [pids] + affected.update(pids) + if affected & wanted: + t = v["Title"] + rows.append( + {"CVE": v["CVE"], "Title": t["Value"] if isinstance(t, dict) else t} + ) + return rows + + +def get_pt_cve_list_by_platform(month: str, targets: set[str], name: str = None): + """ + :param name: Full or partial name of the platform, e.g. "Windows" or "Windows Server + :param month: Patch Tuesday month (YYYY-MMM) + :param targets: set of ProductIDs + """ + data = download(month) + targets, name = pick_ids(data, name, targets) + records = collect_cves(data, targets) + return pl.DataFrame(records).sort("CVE"), name, targets + + +def print_cve_list(df: pl.DataFrame) -> str: + wrapped = ( + df.with_columns( + pl.col("Title").map_elements( + lambda s: "\n".join(textwrap.wrap(s, 60)), + return_dtype=pl.Utf8, + ) + ) + ) + + with pl.Config(tbl_rows=wrapped.height, + tbl_cols=wrapped.width, + fmt_str_lengths=200): + return str(wrapped) + + +def main(argv: list[str] | None = None) -> None: + ap = argparse.ArgumentParser() + ap.add_argument("--month") + ap.add_argument("--platform-name") + ap.add_argument("--product-ids") + ap.add_argument("--cve") + args = ap.parse_args(argv) + + if not args.month: + args.month = input("Patch Tuesday month (YYYY-MMM): ").strip() + if not (args.platform_name or args.product_ids): + if input("Filter by name? [y/N] ").lower().startswith("y"): + args.platform_name = input("Platform name substring: ").strip() + else: + args.product_ids = input("Comma-separated ProductIDs: ").strip() + + explicit = {s for s in (args.product_ids or "").split(",") if s} + data = download(args.month) + targets, targets_names = pick_ids(data, args.platform_name, explicit) + if not targets: + sys.exit("No matching ProductIDs") + + records = collect_cves(data, targets) + if not records: + sys.exit("No CVEs found") + + df = pl.DataFrame(records).sort("CVE") + + print(f'Results for {targets_names}') + print_cve_list(df) + + if args.csv: + out = Path(args.csv).expanduser() + df.write_csv(out) + print("Saved →", out) + + +if __name__ == "__main__": + main() diff --git a/patch_downloader/get_os_data.py b/patch_downloader/get_os_data.py new file mode 100644 index 0000000..494268d --- /dev/null +++ b/patch_downloader/get_os_data.py @@ -0,0 +1,151 @@ +import ctypes +import winreg +from ctypes.wintypes import DWORD, WORD, BYTE, WCHAR +import sys +from common import logger, console +import requests + +from patch_downloader.filter_by_platform import pick_ids + + +class SYSTEM_INFO(ctypes.Structure): + _fields_ = [ + ("wProcessorArchitecture", WORD), + ("wReserved", WORD), + ("dwPageSize", DWORD), + ("lpMinimumApplicationAddress", ctypes.c_void_p), + ("lpMaximumApplicationAddress", ctypes.c_void_p), + ("dwActiveProcessorMask", ctypes.c_void_p), + ("dwNumberOfProcessors", DWORD), + ("dwProcessorType", DWORD), + ("dwAllocationGranularity", DWORD), + ("wProcessorLevel", WORD), + ("wProcessorRevision", WORD), + ] + + +RtlGetVersion = ctypes.windll.ntdll.RtlGetVersion # always returns real version +GetProductInfo = ctypes.windll.kernel32.GetProductInfo +GetNativeSystemInfo = ctypes.windll.kernel32.GetNativeSystemInfo + + +# --- Registry helper ------------------------------------------------------- +def get_reg_version(field) -> str | None: + path = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion" + try: + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path) as k: + return winreg.QueryValueEx(k, field)[0] + except FileNotFoundError: + return None + + +def processor_arch_tokens(arch_map: dict[int, tuple[str]]): + si = SYSTEM_INFO() + GetNativeSystemInfo(ctypes.byref(si)) + return arch_map.get(si.wProcessorArchitecture, ("unknown",)) + + +# --- Main OS-identification routine --------------------------------------- +def get_windows_version_tokens(): + version_tokens = set() + info = sys.getwindowsversion() + + version_tokens.update([get_reg_version("DisplayVersion")]) + version_tokens.update(processor_arch_tokens( + { + 0: ("x86", "32-bit"), + 5: ("arm",), + 9: ("x64", "64-bit", "amd64", "x64-based"), + 12: ("arm64", "ARM64-based"), + } + )) + version_tokens.update(f"service pack {info.service_pack_major}".split() if info.service_pack_major else ()) + + if info.product_type > 1: + version_tokens.update('server') + product_name = get_reg_version('ProductName') + version_tokens.update(product_name.lower().split() if isinstance(product_name, str) else ()) + else: + match (info.major, info.minor): + case (10, 0): + name = "windows 11" if info.build >= 22000 else "windows 10" + case (6, 3): + name = "windows 8.1" + case (6, 3): + name = "windows 8.1" + case (6, 2): + name = "windows 8" + case (6, 1): + name = "windows 7" + case _: + name = f"windows {info.major}.{info.minor}" + + version_tokens.update(name.split()) + + # TODO: add core server https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/hh846315(v=vs.85) + + return version_tokens + + +def is_int(value: str | int): + if isinstance(value, int): + return True + elif isinstance(value, str): + try: + v = int(value) + return True + except ValueError: + pass + + return False + + +def get_cvrf_data(cvrf_id="2025-Apr"): + url = f"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/{cvrf_id}" + headers = {"Accept": "application/json"} + data = requests.get(url, headers=headers, timeout=30).json() + return data + +def load_product_tree(data) -> dict[int, str]: + # Build name → ID map + return { + int(p["ProductID"]): p["Value"] + for p in data["ProductTree"]["FullProductName"] + if is_int(p["ProductID"]) + } + + +def get_cvrf_product_name_and_id(machine_id_t: set | None = None): + if machine_id_t is None: + machine_id_t = get_windows_version_tokens() + + console.info(f'Windows version tokens: {machine_id_t}') + data = get_cvrf_data() + if input("Choose from a list? [y/N] ").lower().startswith("y"): + res = pick_ids(cvrf=data, query=input('Product name: '), ids=set()) + if res[0]: + name = res[1][0] + id = [i for i in res[0]][0] + logger.info( + f'CVRF product name is {res}' + ) + else: + p_tree = load_product_tree(data) + rank = 0 + name = '' + id = 0 + for k, v in p_tree.items(): + n = v.replace('(', '').replace(')', '') + r = len(machine_id_t & set(n.split())) + if r > rank: + rank = r + name = v + id = k + + return name, id + + +if __name__ == "__main__": + name, id = get_cvrf_product_name_and_id({'server', '2025'}) + + print(f'CVRF product name is {name} with id {id}') diff --git a/patch_downloader/kb_downloader.py b/patch_downloader/kb_downloader.py new file mode 100644 index 0000000..9ea6d36 --- /dev/null +++ b/patch_downloader/kb_downloader.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +import argparse +import re +import sys +from pathlib import Path +from requests_html import HTMLSession + +from common import retry_on_exception, logger, resource_lock, console + +SEARCH_URL = "https://www.catalog.update.microsoft.com/Search.aspx?q={}" +DL_URL = "https://catalog.update.microsoft.com/DownloadDialog.aspx" +UID_PAT = re.compile(r"goToDetails\(['\"]([0-9a-f\-]{36})['\"]\)") + + +def find_uid(session: HTMLSession, kb: str, product: str) -> str: + resp = session.get(SEARCH_URL.format(kb), timeout=30) + target = 'microsoft server operating system' if 'server' in product.lower() else product.lower() + for a in resp.html.find("a[onclick^='goToDetails']"): + if target in a.text.lower(): + m = UID_PAT.search(a.attrs.get("onclick", "")) + if m: + return m.group(1) + raise RuntimeError("[x] UID not found") + + +def find_msu(session: HTMLSession, uid: str, kb: str) -> str: + payload = {"updateIDs": f'[{{"uidInfo":"{uid}","updateID":"{uid}"}}]'} + html = session.post(DL_URL, data=payload, timeout=30).text + msu_re = re.compile(rf"https://[^'\"\s]*kb{kb}[^'\"\s]*\.(?:msu|cab)", re.I) + m = msu_re.search(html) + if not m: + raise RuntimeError("[x] .msu link not found") + url = m.group(0) + return url + + +def grab(session: HTMLSession, url: str, out_dir: Path, overwrite: bool) -> Path: + out_dir.mkdir(parents=True, exist_ok=True) + name = url.rsplit('/', 1)[1] + dest = out_dir / name + + with resource_lock(dest.resolve()): + if not overwrite and dest.exists(): + console.info(f'[+] Update installer exist on disk [{name}]') + return dest + + with session.get(url, stream=True, timeout=30) as r, dest.open("wb") as f: + r.raise_for_status() + console.info(f"[*] Downloading {name}") + for chunk in r.iter_content(8192): + if chunk: + f.write(chunk) + + size_mb = dest.stat().st_size // 1_048_576 + logger.info(f"[+] {name} ({size_mb} MB) downloaded to {out_dir.resolve()}") + return dest + + +@retry_on_exception +def download_kb(kb, product, out_dir: Path, overwrite=False) -> Path: + files = [f for f in out_dir.glob("*.msu") if kb in f.stem and f.is_file()] + if len(files) == 1: + console.info(f'[+] Update installer exist on disk [{files[0].name}]') + return files[0] + + kb_num = kb.lower().lstrip("kb") + session = HTMLSession() + session.cookies.set('display-culture', 'en-US') + + uid = find_uid(session, kb, product) + url = find_msu(session, uid, kb_num) + dest = grab(session, url, out_dir, overwrite) + return dest + + +def main(argv: list[str] | None = None) -> None: + ap = argparse.ArgumentParser() + ap.add_argument("kb", help="KBxxxxx or digits") + ap.add_argument("product", help="Product substring from catalog row") + ap.add_argument("-o", "--outdir", default=".") + args = ap.parse_args(argv) + + download_kb(args.kb, args.product, Path(args.outdir)) + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + sys.exit("\n[x] Interrupted") diff --git a/patch_extractor/UpdateCompression.dll b/patch_extractor/UpdateCompression.dll new file mode 100644 index 0000000..424cb4c Binary files /dev/null and b/patch_extractor/UpdateCompression.dll differ diff --git a/patch_extractor/__init__.py b/patch_extractor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/patch_extractor/archive_managers.py b/patch_extractor/archive_managers.py new file mode 100644 index 0000000..b1bb362 --- /dev/null +++ b/patch_extractor/archive_managers.py @@ -0,0 +1,260 @@ +# archive_manager.py +import asyncio +import json +import mmap +import os +import struct +import tempfile +from pathlib import Path +from typing import Type +import xml.etree.ElementTree as ET +from typing import Optional, TypedDict + +from common import logger +from patch_extractor.patch_tools import PatchTools + + +class HashDict(TypedDict, total=False): + alg: Optional[str] + value: Optional[str] + + +class DeltaDict(TypedDict, total=False): + offset: Optional[int] + length: Optional[int] + hash: HashDict + + +class FileResult(TypedDict, total=False): + hash: HashDict + delta: DeltaDict + + +_7ZIP_PATH = 'C:/Program Files/7-Zip/7z.exe' + + +class ArchiveManager: + @staticmethod + async def run_process(cmd, capture=True): + if capture: + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + return process.returncode, stdout.decode('utf-8', errors='replace'), stderr.decode('utf-8', + errors='replace') + else: + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.PIPE + ) + _, stderr = await process.communicate() + return process.returncode, None, stderr.decode('utf-8', errors='replace') + + @staticmethod + async def stream_process(cmd): + process = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + + while True: + line = await process.stdout.readline() + if not line: + break + yield line.decode('utf-8', errors='replace') + + stderr = await process.stderr.read() + stderr_str = stderr.decode('utf-8', errors='replace') + return_code = await process.wait() + + yield return_code, stderr_str + + async def extract_file(self, archive: Path, dest, flat=False): + raise NotImplementedError() + + async def list_files(self, archive: Path): + raise NotImplementedError() + + async def extract_all(self, archive: Path, dest, flat=False): + raise NotImplementedError() + + async def extract_by_list(self, archive: Path, files, dest, flat=False): + raise NotImplementedError() + + @staticmethod + def get_supported_ext() -> list[str]: + raise NotImplementedError() + + +class PSF(ArchiveManager): + @staticmethod + def get_supported_ext(): + return ['.psf'] + + @staticmethod + def _parse_psf_xml(xml_data): + root = ET.fromstring(xml_data) + + if not root.tag.endswith('Container') or root.attrib.get('type') != 'PSF': + raise ValueError("XML does not represent a PSF Container") + + ns = {'c': 'urn:ContainerIndex'} + files_elem = root.find('c:Files', ns) + if files_elem is None: + raise ValueError("No <Files> section found in the XML") + + result: dict[str, FileResult] = {} + for file in files_elem.findall('c:File', ns): + hash_elem = file.find('c:Hash', ns) + delta_elem = file.find('c:Delta', ns) + source_elem = delta_elem.find('c:Source', ns) + source_hash = source_elem.find('c:Hash', ns) if source_elem is not None else None + + # Build dictionary structure + result[file.get('name')] = FileResult( + hash=HashDict(alg=hash_elem.get('alg') if hash_elem is not None else None, + value=hash_elem.get( + 'value') if hash_elem is not None else None), + delta=DeltaDict(offset=int(source_elem.get( + 'offset')) if source_elem is not None else None, + length=int(source_elem.get( + 'length')) if source_elem is not None else None, + hash=HashDict(alg=source_hash.get( + 'alg') if source_hash is not None else None, + value=source_hash.get( + 'value') if source_hash is not None else None))) + + return result + + @staticmethod + def _get_manifest(psf: mmap.mmap): + manifest_offset = struct.unpack("<I", psf[40:44])[0] + manifest_length = struct.unpack("<I", psf[44:48])[0] + pt = PatchTools() + manifest = pt.apply(None, memoryview(psf[manifest_offset: manifest_offset + manifest_length])) + return manifest + + async def _list_files(self, psf) -> dict[str, FileResult]: + if psf[:7] != b'PSTREAM': + raise AssertionError('Not a valid PSF file') + manifest = self._get_manifest(psf) + return await asyncio.to_thread(self._parse_psf_xml, manifest) + + async def list_files(self, archive: Path): + dd = archive.parent.parent / 'DesktopDeployment.cab' / 'UpdateCompression.dll' + if dd.exists(): + PatchTools(dd) + + mm = PatchTools.map_file(archive) + results = await self._list_files(mm) + return 0, [file for file in results], None + + async def extract_by_list(self, archive: Path, files, dest: Path, flat=False): + mm = PatchTools.map_file(archive) + results: dict[str, FileResult] = await self._list_files(mm) + dest.mkdir(parents=True, exist_ok=True) + for path, file in results.items(): + if path in files: + try: + mm.seek(file['delta']['offset']) + data = mm.read(file['delta']['length']) + try: + file_path = dest / (Path(path).name if flat else path) + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.write_bytes(data) + except Exception as e: + print(f"Error writing to destination file: {e}") + except Exception as e: + print(f"Error reading the memory region: {e}") + return + + try: + (dest / 'manifest.json').write_text(json.dumps(results)) + except IOError: + logger.error('Cannot write psf manifest') + pass + + +class SevenZip(ArchiveManager): + @staticmethod + def get_supported_ext(): + return ['.7z', '.zip', '.rar', '.tar', '.gz', '.bz2', '.xz', + '.iso', '.wim', '.cab', '.msu', '.msi', '.esd', '.arj', + '.cpio', '.deb', '.lzh', '.lzma', '.rpm', '.udf', + '.vhd', '.vhdx', '.xar', '.z'] + + def __init__(self, executable_path=_7ZIP_PATH): + self.executable = executable_path + + async def extract_file(self, archive: Path, dest, flat=False): + cmd = [self.executable, "x", archive, "-y"] + if dest: + cmd.append(f"-o{dest}") + os.makedirs(dest, exist_ok=True) + return await self.run_process(cmd) + + async def list_files(self, archive: Path): + cmd = [self.executable, "l", "-slt", "-ba", archive] + + files = [] + return_code = -1 + stderr = "" + + async for line_or_result in self.stream_process(cmd): + # Check if this is the final yield containing return code and stderr + if isinstance(line_or_result, tuple): + return_code, stderr = line_or_result + break + + line = line_or_result.strip() + if not line or not line.startswith('Path ='): + continue + + files.append(line[7:].strip()) + + if return_code == 0: + return return_code, files, stderr + + return return_code, [], stderr + + async def extract_by_list(self, archive: Path, files, dest, flat=False): + list_file = None + with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as tmp: + tmp.write('\n'.join(files)) + tmp.flush() + list_file = tmp.name + + if list_file: + try: + cmd = [ + self.executable, + 'e' if flat else 'x', + archive, + f"-i@{list_file}", + f"-o{dest}", + "-y" + ] + + return await self.run_process(cmd, False) + finally: + os.remove(list_file) + + +class Selector: + managers: list[ArchiveManager] = [] + + def add_manager(self, manager: Type[ArchiveManager]): + self.managers.append(manager()) + + def get_manager(self, ext: str) -> ArchiveManager | None: + for m in self.managers: + if ext in m.get_supported_ext(): + return m + + return None + + def get_supported(self): + supported = [] + for m in self.managers: + supported.extend(m.get_supported_ext()) + + return supported diff --git a/patch_extractor/extractor.py b/patch_extractor/extractor.py new file mode 100644 index 0000000..4dbcfc9 --- /dev/null +++ b/patch_extractor/extractor.py @@ -0,0 +1,182 @@ +# main.py +import asyncio +import shutil +import time +from enum import Enum +from pathlib import Path + +from patch_extractor.manifest_extractor import ManifestExtractor +from common import logger, get_latest_servicingstack_folder, EXECUTABLE_EXTENSIONS, console +from patch_extractor.archive_managers import Selector, SevenZip, ArchiveManager, PSF + + +class ARCH(Enum): + all = 1 + x64 = 2 + x86 = 3 + msil = 4 + wow64 = 5 + + +class KBExtractor: + def __init__(self, selector: Selector, n_workers=5): + self.dest: Path = Path() + self.stop_flag = False + self.selector = selector + self.archives = asyncio.Queue() + self.supported_ext = selector.get_supported() + + self.workers = [] + for i in range(n_workers): + self.workers.append(asyncio.create_task(self._extraction_worker())) + + self.extracted: list[Path] = [] + self.keep_manifests = False + + def extract(self, kb_path, dest, manifests: bool): + self.keep_manifests = manifests + + if dest: + self.dest = Path(dest) + else: + kb = Path(kb_path) + self.dest = kb.parent.absolute() / f'extracted_{kb.name}' + + self.archives.put_nowait(kb_path) + + async def _extraction_worker(self): + while True: + try: + archive = await self.archives.get() + except asyncio.CancelledError: + logger.debug('worker exits') + return + except Exception as e: + raise e + + try: + await self._extract(archive) + finally: + self.archives.task_done() + + async def _extract_files(self, manager: ArchiveManager, archive: Path, files: list[str], sub_dir, flat=False): + return await manager.extract_by_list(archive, files, Path(self.dest) / sub_dir, flat) + + async def _extract_nested_archives(self, manager, archive, files): + nested = [x for x in files if Path(x).suffix.lower() in self.supported_ext] + if nested: + # logger.info(nested) + dest = Path('archives') / archive.name + await self._extract_files(manager, archive, nested, dest, True) + + for nested_file in nested: + nested_path = Path(self.dest) / dest / Path(nested_file).name + if nested_path.exists(): + self.extracted.append(dest / Path(nested_file).name) + self.archives.put_nowait(nested_path) + + async def _extract_executables(self, manager, archive, files): + executables = [x for x in files if Path(x).suffix.lower() in EXECUTABLE_EXTENSIONS] + if executables: + await self._extract_files(manager, archive, executables, archive.name) + for e in executables: + self.extracted.append(Path(archive.name) / e) + + async def _extract_manifests(self, manager, archive, files): + manifests = [x for x in files if Path(x).suffix.lower() in ['.manifest']] + if manifests: + # logger.info(executables) + await self._extract_files(manager, archive, manifests, 'manifests', True) + extractor = ManifestExtractor(get_latest_servicingstack_folder() / 'wcp.dll') + for m in manifests: + try: + m_path = Path('manifests') / Path(m).name + self.extracted.append(m_path) + extractor.extract(self.dest / m_path) + except (IOError, RuntimeError): + logger.debug(f'Failed to extract the manifest {m}') + + async def _extract(self, path): + archive = Path(path) + manager = self.selector.get_manager(archive.suffix.lower()) + if not manager: + logger.error(f'Archive {archive.suffix.lower()} is not supported') + return + + logger.info(f'Extract {archive}') + _, files, stderr = await manager.list_files(archive) + if not files: + logger.error(f'Failed to extract {archive} with error: {" ".join(stderr.split())}') + return + + tasks = [self._extract_nested_archives(manager, archive, files), + self._extract_executables(manager, archive, files)] + + if self.keep_manifests: + tasks.append(self._extract_manifests(manager, archive, files)) + + await asyncio.gather(*tasks) + + async def join(self): + await self.archives.join() + for worker in self.workers: + worker.cancel() + await asyncio.gather(*self.workers) + + +async def aextract(kb_path: Path, dest: Path | None, manifests: bool): + kb = Path(kb_path) + dest = dest or kb.parent.absolute() / f'extracted_{kb.name}' + if dest.exists() and (dest / 'report.txt').exists(): + console.info(f'[+] {kb.name} extracted folder was found, skip extraction') + return + + start_time = time.time() + selector = Selector() + selector.add_manager(SevenZip) + selector.add_manager(PSF) + extractor = KBExtractor(selector) + extractor.extract(kb_path, dest, manifests) + await extractor.join() + + elapsed_time = time.time() - start_time + console.info(f"[*] Extraction finished within {elapsed_time:.2f} seconds") + + archives = extractor.dest / 'archives' + shutil.rmtree(archives) + + report = (extractor.dest / 'report.txt').absolute() + with open(report, 'w') as f: + for e in extractor.extracted: + if e.suffix.lower() in EXECUTABLE_EXTENSIONS: + f.write(str(e) + '\n') + + # __report_breakdown(extractor) + + console.info(f'[+] Extracted {len(extractor.extracted)} files, report in {report}') + + +def __report_breakdown(extractor): + i = 0 + file_count = 0 + + while i < len(extractor.extracted): + with open(extractor.dest / f'report_amd64_{file_count}.txt', 'w') as f: + lines_written = 0 + for e in extractor.extracted[i:]: + rel = str(e) + i += 1 + if 'amd64' in rel: + f.write(rel + '\n') + lines_written += 1 + if lines_written >= 1000: + break + file_count += 1 + + +def extract(kb_path, dest='', manifests=False, arch=ARCH.x64): + asyncio.run(aextract(kb_path, dest, manifests, arch)) + + +if __name__ == '__main__': + extract('') diff --git a/patch_extractor/manifest_extractor.py b/patch_extractor/manifest_extractor.py new file mode 100644 index 0000000..619c8ee --- /dev/null +++ b/patch_extractor/manifest_extractor.py @@ -0,0 +1,125 @@ +import ctypes +from ctypes import wintypes +from pathlib import Path + +from common import logger + + +class ManifestExtractor: + class BlobData(ctypes.Structure): + _pack_ = 1 + _fields_ = [ + ("length", ctypes.c_size_t), + ("fill", ctypes.c_size_t), + ("pData", ctypes.c_void_p) + ] + + def __init__(self, dll_path=Path("wcp.dll"), verbose=False): + + # Load wcp.dll + try: + self.wcp = ctypes.WinDLL(str(dll_path)) + logger.debug(f"Successfully loaded {dll_path}") + self._setup_functions() + except Exception as e: + raise RuntimeError(f"Failed to load WCP DLL: {e}") + + def _setup_functions(self): + # GetCompressedFileType + self.GetCompressedFileType = self.wcp["?GetCompressedFileType@Rtl@WCP@Windows@@YAKPEBU_LBLOB@@@Z"] + self.GetCompressedFileType.restype = ctypes.c_ulong + self.GetCompressedFileType.argtypes = [ctypes.POINTER(self.BlobData)] + + # InitializeDeltaCompressor + self.InitializeDeltaCompressor = self.wcp["?InitializeDeltaCompressor@Rtl@Windows@@YAJPEAX@Z"] + self.InitializeDeltaCompressor.restype = ctypes.c_long + self.InitializeDeltaCompressor.argtypes = [ctypes.c_void_p] + + # DeltaDecompressBuffer + self.DeltaDecompressBuffer = self.wcp[ + "?DeltaDecompressBuffer@Rtl@Windows@@YAJKPEAU_LBLOB@@_K0PEAVAutoDeltaBlob@12@@Z"] + self.DeltaDecompressBuffer.restype = ctypes.c_long + self.DeltaDecompressBuffer.argtypes = [ + ctypes.c_ulong, # DeltaFlagType + ctypes.c_void_p, # pDictionary + ctypes.c_ulong, # headerSize + ctypes.POINTER(self.BlobData), # inData + ctypes.POINTER(self.BlobData) # outData + ] + + # LoadFirstResourceLanguageAgnostic + self.LoadFirstResourceLanguageAgnostic = self.wcp[ + "?LoadFirstResourceLanguageAgnostic@Rtl@Windows@@YAJKPEAUHINSTANCE__@@PEBG1PEAU_LBLOB@@@Z"] + self.LoadFirstResourceLanguageAgnostic.restype = ctypes.c_long + self.LoadFirstResourceLanguageAgnostic.argtypes = [ + ctypes.c_ulong, # unused + wintypes.HINSTANCE, # hModule + ctypes.c_void_p, # lpType (special value) + ctypes.c_void_p, # lpName (special value) + ctypes.c_void_p # pOutDict + ] + + def extract(self, input_path, output_path=None): + if output_path is None: + output_path = input_path + + try: + with open(input_path, "rb") as f: + manifest_data = f.read() + except Exception as e: + raise IOError(f"Failed to read input file: {e}") + + data_size = len(manifest_data) + logger.debug(f"Input file size: {data_size} bytes") + + manifest_buffer = ctypes.create_string_buffer(manifest_data, data_size) + + in_data = self.BlobData() + in_data.length = data_size + in_data.fill = data_size + in_data.pData = ctypes.cast(manifest_buffer, ctypes.c_void_p) + + file_type = self.GetCompressedFileType(ctypes.byref(in_data)) + logger.debug(f"Compression type: {file_type}") + + result = self.InitializeDeltaCompressor(None) + if result < 0: + raise RuntimeError("Failed to initialize delta compressor") + + dict_data = (ctypes.c_uint64 * 3)() + + result = self.LoadFirstResourceLanguageAgnostic( + 0, # unused + self.wcp._handle, # HMODULE + ctypes.c_void_p(0x266), # lpType (special value) + ctypes.c_void_p(1), # lpName (special value) + ctypes.byref(dict_data) + ) + + if result < 0: + raise RuntimeError("Failed to load resource dictionary") + + out_data = self.BlobData() + + result = self.DeltaDecompressBuffer( + 2, # type (2 from original code) + ctypes.byref(dict_data), # dictionary + 4, # headerSize (4 from original code) + ctypes.byref(in_data), # input data + ctypes.byref(out_data) # output data + ) + + if result < 0: + raise RuntimeError("Failed to decompress data") + + outbuffer = ctypes.string_at(out_data.pData, out_data.length) + logger.debug(f"Decompressed size: {out_data.length} bytes") + + try: + with open(output_path, "wb") as outfile: + outfile.write(outbuffer) + logger.debug(f"Decompressed data written to {output_path}") + except Exception as e: + raise IOError(f"Failed to write output file: {e}") + + return outbuffer diff --git a/patch_extractor/patch_tools.py b/patch_extractor/patch_tools.py new file mode 100644 index 0000000..a899232 --- /dev/null +++ b/patch_extractor/patch_tools.py @@ -0,0 +1,287 @@ +import ctypes +import mmap +import os +import sys +import zlib +from ctypes import (CDLL, FormatError, POINTER, LittleEndianStructure, Union, byref, + c_size_t, c_ubyte, c_uint64, cast, windll, wintypes) +from pathlib import Path +import glob +import xml.etree.ElementTree as ET + +from common import get_winsxs, logger + + +class DeltaInput(LittleEndianStructure): + class StartUnion(Union): + _fields_ = [('lpcStart', wintypes.LPVOID), + ('lpStart', wintypes.LPVOID)] + + _anonymous_ = ('start_union',) + _fields_ = [('start_union', StartUnion), + ('uSize', c_size_t), + ('Editable', wintypes.BOOL)] + + +class DeltaOutput(LittleEndianStructure): + _fields_ = [('lpStart', wintypes.LPVOID), + ('uSize', c_size_t)] + + +def cast_void_p(buffer: memoryview): + if isinstance(buffer.obj, mmap.mmap): + addr = ctypes.addressof(ctypes.c_byte.from_buffer(buffer.obj)) + return ctypes.wintypes.LPVOID(addr) + if isinstance(buffer.obj, bytes): + return ctypes.cast(buffer.obj, ctypes.wintypes.LPVOID) + + raise RuntimeError(f"Underline object is neither bytes or mmap") + + +class PatchTools: + delta_modules: list[ctypes.WinDLL] = [] + + @staticmethod + def load_delta_modules(modules: list[str]): + for dll in modules: + if any(m for m in PatchTools.delta_modules if m._name == dll): + continue + try: + PatchTools.delta_modules.append(windll.__getattr__(dll)) + except OSError: + logger.debug(f'Failed to load patch tools module {dll}') + + def __init__(self, module: Path | None = None): + if not PatchTools.delta_modules: + default_dlls = [str(Path(__file__).resolve().parent / "UpdateCompression.dll"), 'UpdateCompression', 'msdelta'] + PatchTools.load_delta_modules(default_dlls) + + if not PatchTools.delta_modules: + raise RuntimeError( + "Failed to load required DLL. Please ensure UpdateCompression.dll or " + "msdelta.dll is available.") + + if module: + PatchTools.load_delta_modules([str(module.resolve())]) + + self.apply_delta, self.free_delta = self._load_api(PatchTools.delta_modules[0]) + self.winsxs = get_winsxs() + + @staticmethod + def _load_api(module): + apply_delta = module.ApplyDeltaB + apply_delta.argtypes = [c_uint64, DeltaInput, DeltaInput, POINTER(DeltaOutput)] + apply_delta.restype = wintypes.BOOL + + free_delta = module.DeltaFree + free_delta.argtypes = [wintypes.LPVOID] + free_delta.restype = wintypes.BOOL + + return apply_delta, free_delta + + @staticmethod + def is_patch(patch: memoryview | bytes): + try: + PatchTools.validate_patch(memoryview(patch)) + except (ValueError, TypeError): + return False + + return True + + @staticmethod + def validate_patch(patch: memoryview): + if len(patch) < 8: + raise ValueError('Patch size is too small') + + sig = patch[:6].tobytes() + + if not sig.startswith(b"PA") or sig.endswith(b'PA'): + expected_crc = int.from_bytes(patch[:4].tobytes(), 'little') + patch_data: memoryview = memoryview(patch.obj[4:]) + if zlib.crc32(patch_data) != expected_crc: + raise ValueError("CRC32 check failed. Patch corrupted or invalid") + elif sig.startswith(b"PA"): + patch_data: memoryview = patch + else: + raise ValueError(f"Invalid patch format in {sig}") + + return patch_data + + @staticmethod + def map_file(path, offset=0, length=0, access=mmap.ACCESS_COPY): + with open(path, 'r+b') as f: + return mmap.mmap(f.fileno(), length, offset=offset, access=access) + + def _apply(self, buffer: memoryview | None, patch: memoryview): + patch_input = DeltaInput() + + patch_data = self.validate_patch(patch) + patch_input.lpStart, patch_input.uSize = cast_void_p(patch_data), patch_data.nbytes + patch_input.Editable = False + + source = DeltaInput() + if buffer is not None: + source.lpStart = cast_void_p(buffer) + source.uSize = buffer.nbytes + source.Editable = False + else: + source.lpStart = None + source.uSize = 0 + source.Editable = False + + output = DeltaOutput() + + flags = 0 # DELTA_APPLY_FLAG_ALLOW_PA19 = 1 + success = self.apply_delta(flags, source, patch_input, byref(output)) + + if not success: + error_code = ctypes.windll.kernel32.GetLastError() + raise RuntimeError(f"Failed to apply patch: 0x{error_code:X}") + + # Extract the result + result_size = output.uSize + result_ptr = output.lpStart + + # Copy the result to a Python bytes object + result_array = (c_ubyte * result_size).from_address(result_ptr) + result = bytes(result_array) + + # Free the memory allocated by ApplyDeltaB + self.free_delta(result_ptr) + + return result + + def apply(self, buffer: memoryview | None, patch: memoryview) -> bytes | None: + gen = (x for x in PatchTools.delta_modules) + try: + result = self._apply(buffer, patch) + if result: + return result + except ValueError as e: + logger.error(e) + raise RuntimeError('This is not a valid patch') + except RuntimeError as e: + try: + m = next(gen) + self.apply_delta, self.free_delta = self._load_api(m) + except StopIteration: + logger.error(e) + raise e + + def find_base_and_load(self, name): + if not self.winsxs: + raise RuntimeError("WinSxS directory not found") + + # Find matching directories sorted by newest first + matching_dirs = sorted(self.winsxs.glob(f"*_{name}_*"), + key=lambda p: p.stat().st_ctime, reverse=True) + + if not matching_dirs: + raise FileNotFoundError(f"No matching directory found for {name}") + + for dir_path in matching_dirs: + # Direct DLL check + dll_path = dir_path / name + if dll_path.exists(): + return dll_path.read_bytes() + + r_dir, f_dir = dir_path / "r", dir_path / "f" + reverse_patches = sorted(r_dir.glob("*.pa_")) if r_dir.exists() and r_dir.is_dir() else [] + + if not reverse_patches: + continue + + # Find base version to patch + try: + current = None + if f_dir.exists() and f_dir.is_dir(): + forward_dlls = list(f_dir.glob(f"{name}")) + current = forward_dlls[0].read_bytes() if forward_dlls else None + + if not current: + system32_path = Path(os.environ["SystemRoot"]) / "System32" / name + current = system32_path.read_bytes() if system32_path.exists() else None + + if not current: + continue + + # Apply reverse patches + for patch_path in reverse_patches: + try: + current = self.apply(current, patch_path.read_bytes()) + except RuntimeError as e: + print(f"Warning: Failed to apply patch {patch_path}: {e}") + + return current + + except (FileNotFoundError, IOError): + continue + + raise FileNotFoundError(f"Could not find or reconstruct base for {name}") + + def get_manifest(self, name): + if not self.winsxs: + raise RuntimeError("WinSxS directory not found") + + manifests_dir = self.winsxs / "Manifests" + if not manifests_dir.exists(): + raise FileNotFoundError("Manifests directory not found") + + # Find matching manifests + pattern = f"*_{name}_*.manifest" + matching_manifests = list(manifests_dir.glob(pattern)) + + if not matching_manifests: + raise FileNotFoundError(f"No manifest found for {name}") + + # Sort by creation time (newest first) + matching_manifests.sort(key=lambda p: p.stat().st_ctime, reverse=True) + + # Return the content of the most recent manifest + with open(matching_manifests[0], "rb") as f: + return f.read() + + def parse_manifest(self, manifest_data): + try: + root = ET.fromstring(manifest_data.decode('utf-8')) + + # Define XML namespaces + namespaces = { + 'asm': 'urn:schemas-microsoft-com:asm.v1', + 'win': 'urn:schemas-microsoft-com:asm.v3' + } + + # Extract basic info + identity = root.find('.//asm:identity', namespaces) + if identity is not None: + name = identity.get('name') + version = identity.get('version') + architecture = identity.get('processorArchitecture') + public_key_token = identity.get('publicKeyToken') + else: + name = version = architecture = public_key_token = None + + # Extract file info + files = [] + for file_elem in root.findall('.//win:file', namespaces): + files.append({ + 'name': file_elem.get('name'), + 'hash': file_elem.get('hash'), + 'hashalg': file_elem.get('hashalg') + }) + + return { + 'name': name, + 'version': version, + 'architecture': architecture, + 'public_key_token': public_key_token, + 'files': files + } + except Exception as e: + print(f"Error parsing manifest: {e}") + return None + +# p = PatchTools() +# mm = PatchTools.map_file( +# r'..\test\extracted_old_windows11.0-kb5051987-x64_199ed7806a74fe78e3b0ef4f2073760000f71972.msu\archives\Windows11.0-KB5051987-x64-3.psf') +# p.apply(None, mm) diff --git a/patchdiff_ai.py b/patchdiff_ai.py new file mode 100644 index 0000000..a5e44e9 --- /dev/null +++ b/patchdiff_ai.py @@ -0,0 +1,47 @@ +import asyncio +import sys + +from agent_tools.vector_store import VectorStore +from args import get_cve_list +from common import Timer, Threshold, logger, console +from supervisor.supervisor import Supervisor + + +# snapshot2 = tracemalloc.take_snapshot() + + +async def run(cve: str, config: dict = None): + pd_ai = Supervisor() + async for step in pd_ai.run(cve=cve, config=config): + pass + + return True + + +async def patch_wedensday_assistant(argv: list[str]): + cve_list = get_cve_list(argv) + if cve_list is None or len(cve_list) == 0: + return + + with Timer('Patch Wednesday Assistant'): + config = {'interrupt': False if len(cve_list) > 1 else True, + 'threshold': Threshold( + candidates=7.5, + security_modification=0.25, + report=0.1 + )} + console.info(f'[*] Start the system with config: {config}') + tasks = [await asyncio.to_thread(run, + cve=c, + config=config) for c in cve_list] + results = await asyncio.gather(*tasks, return_exceptions=True) + logger.debug(f'Results: {results}') + + console.info('[+] Done.') + + +if __name__ == "__main__": + try: + asyncio.run(patch_wedensday_assistant(sys.argv[1:]), debug=False) + except (EOFError, KeyboardInterrupt): + pass diff --git a/re_agent/__init__.py b/re_agent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/re_agent/revearse_engineer_agent.py b/re_agent/revearse_engineer_agent.py new file mode 100644 index 0000000..8a1ffc4 --- /dev/null +++ b/re_agent/revearse_engineer_agent.py @@ -0,0 +1,111 @@ +import asyncio + +from dataclasses import dataclass +from pathlib import Path + +from langgraph.graph import StateGraph + +from agent import Agent +from common import StateInfo, Timer, PatchStoreEntry, logger, Artifact, console +from defaultdataclass import defaultdataclass + +from patch_analysis import ida_analysis +from patch_analysis.bindiff_analysis import analyze_diff + +from bindiff import BinDiff + + +@defaultdataclass +class ReverseEngineeringContext: + state_info: StateInfo + primary_file: PatchStoreEntry + secondary_file: PatchStoreEntry + diff: BinDiff + + +@defaultdataclass +class ReverseEngineeringOutput: + artifacts: list[Artifact] + + +class ReverseEngineering(Agent): + ''' + Agent that can access specific files in the patch_store and analyze them through: + 1. Disassemble and reverse engineering using IDA pro + 2. Export metadata using binexport plugin + 3. Decompile requested function using RVA + 4. Bindiff two binexport files and generate diff file + 5. Using IDAlib as MCP for further analysis (* Future) + ''' + + @dataclass(frozen=True) + class NODES: + analyze = "Analyze binaries" + diff = "Binary diffing" + decompile = "Decompile artifacts" + + def __init__(self): + super().__init__() + + def _build(self): + if self._graph: + return + + builder = StateGraph(state_schema=ReverseEngineeringContext, output=ReverseEngineeringOutput) + + builder.add_node(self.NODES.analyze, self.analyze) + builder.add_node(self.NODES.diff, self.diff) + builder.add_node(self.NODES.decompile, self.decompile) + + builder.set_entry_point(self.NODES.analyze) + builder.add_edge(self.NODES.analyze, self.NODES.diff) + builder.add_edge(self.NODES.diff, self.NODES.decompile) + builder.set_finish_point(self.NODES.decompile) + + self._graph = builder.compile() + + async def analyze(self, context: ReverseEngineeringContext, config): + context.state_info.node.append(self.NODES.analyze) + console.info(f'[*] Start static analysis of {context.primary_file.name}') + with Timer('analyze and export'): + await ida_analysis.batch_analysis(files=[ + ida_analysis.ExecArgs(target=Path(context.primary_file.path)), + ida_analysis.ExecArgs(target=Path(context.secondary_file.path)), + ], + condition=lambda file: not file.target.with_name( + file.target.name + '.BinExport').exists()) + console.info(f'[+] Finish static analysis of {context.primary_file.name}') + + async def diff(self, context: ReverseEngineeringContext): + context.state_info.node.append(self.NODES.diff) + + with Timer('generate bindiff'): + logger.info(f'Diffing {context.primary_file.name} from {context.primary_file.kb} ' + f'against {context.secondary_file.kb}') + + curr_binexport = context.primary_file.path + '.BinExport' + prev_binexport = context.secondary_file.path + '.BinExport' + bindiff_path = f'{context.primary_file.path}.{context.secondary_file.kb}.BinDiff' + + console.info(f'[*] Analyze {context.primary_file.name} code block changes') + diff = await asyncio.to_thread(BinDiff.from_binexport_files, curr_binexport, prev_binexport, bindiff_path) + if not diff: + logger.warning(f'Faild to bindiff {context.primary_file.name}') + + return {'diff': diff} + + async def decompile(self, context: ReverseEngineeringContext): + context.state_info.node.append(self.NODES.decompile) + + if not context.diff: + logger.warning("No diff available to add to vector store") + return {"artifacts": []} + + with Timer('analyze diff and decompile'): + changed = await analyze_diff(context.diff) + console.info(f'[+] {len(changed)} functions modified in {context.primary_file.name}') + + return {"artifacts": [Artifact(primary_file=context.primary_file, + secondary_file=context.secondary_file, + diff=context.diff, + changed=changed)]} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..409f550 --- /dev/null +++ b/readme.md @@ -0,0 +1,360 @@ +# PatchDiff-AI + +**LangGraph‑powered multi‑agent system that turns batches of CVE IDs into fully‑fledged root‑cause reports.** + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Architecture](#architecture) +3. [Prerequisites](#prerequisites) +4. [Installation](#installation) + * [4.1 Python 3.11](#41-python-311) + * [4.2 IDA Pro 8.x](#42-ida-pro-8x) + * [4.3 BinExport + BinDiff 8](#43-binexport--bindiff-8) + * [4.4 Project dependencies](#44-project-dependencies) +5. [Quick Start](#quick-start) +6. [Sample Output](#sample-output) +7. [Extending the Graph](#extending-the-graph) +8. [Contributing](#contributing) +9. [License](#license) + +--- + +## Overview + +`PatchDiff-AI` is a Python-based, LangGraph-driven **multi-agent** system that **automates large-scale reverse-engineering**. Feed it a plain-text list of CVE identifiers, and it **spawns** a supervised multi-agent graph that decompiles the vulnerable component, correlates patched **and** unpatched binaries with BinDiff 8, and **generates** a detailed report for each vulnerability. + +* **Parallel by default** – every CVE runs in its own instance of the graph. +* **Deterministic supervision** – the top-level *Supervisor* node coordinates tool errors and time-outs so rogue sub-agents cannot derail the batch. +* **Extensible** – new analysis stages are a single node away (see [Extending the Graph](#extending-the-graph)). + +> **Supported OS:** **Windows only.** The system targets Microsoft Patch Tuesday updates, but contributors are welcome to extend it to other platforms (e.g., Android) or specific applications. It currently analyzes only CVEs that affect the host OS version.<br> +> **Tested on:** **Windows 11 24H2 x64**; other versions *should* work. + +> ⚠ **Warning:** This project uses `chromadb`, which **has** a known crashing issue on some setups. If you **encounter** *Process finished with exit code -1073741819 (0xC0000005)*, the fault lies in `chromadb`/`HNSWlib`. Switch the virtual-machine engine or use a physical machine. + +--- + +## Architecture + +![Supervisor.png](Supervisor.png) + +Each **Agent** is a subgraph and operates independently. Anyone can extend the system easily by containing the changes to the relevant graph. + +--- + +## Prerequisites + +| Tool | Version | Why | +|---------------|-----------------|-----------------------------------------| +| **Python** | 3.11 x64 | Developed and tested using this version | +| **IDA Pro** | ≥ 8.0 and < 9.0 | Required by BinDiff 8 | +| **BinDiff** | 8.0 | Binary diffing engine | +| **BinExport** | ≥ 12 | IDA plugin that produces .BinDiff files | +| **7-zip** | ≥ 22 | Used to extract the update archives | + +> **Licensing:** IDA Pro is commercial. Buy a legal copy or fork this repo and extend it to Ghidra. + +--- + +## Installation + +### 4.1 Python 3.11 + +Download *Windows x64* installer from [https://www.python.org/downloads/release/python-3119/](https://www.python.org/downloads/release/python-3119/). During setup **enable “Add to PATH”** and **“Install for all users”**. + +```powershell +python --version # should print 3.11.x +``` + +### 4.2 IDA Pro 8.x + +1. Purchase / download IDA Pro 8.x from Hex‑Rays. +2. Run the installer. +3. Keep the default location (e.g. `C:\Program Files\IDA Pro 8.0`). +4. Check if the IDA has access to python by running commands in the GUI shell. + If not, run `idapyswitch` as admin. + +```powershell +"%IDA_PATH%\idapyswitch.exe" +``` + +### 4.3 BinExport + BinDiff 8 + +1. Download BinDiff 8 from Google Security Research. +2. Validate the following DLLs in the IDA *plugins* folder: + * `%appdata%\Hex-Rays\IDA Pro\plugins` + * `bindiff8_ida*.dll` + * `binexport12_ida*.dll` +3. Restart IDA once to verify `Edit → Plugins → BinExport` is present. + +### 4.4 Project dependencies + +```powershell +# clone +> git clone <this repo> +> cd patchdiff_ai + +# isolate +> python -m venv .venv +> .venv\Scripts\activate + +# compile deps (langgraph, python-bindiff, etc.) +> pip install -r requirements.txt +``` + +--- + +## Quick Start +After installation, the fastest way to start your first analysis is to get CVE that affect your system from [MSRC](https://msrc.microsoft.com/update-guide/) +```powershell +> cd patchdiff_ai/ +> python.exe .\patchdiff_ai.py cve CVE-2025-29824 +``` +![single_analysis.gif](rsrc/single_analysis.gif) + +You can also get a list of the CVEs that affect a certain system and even analyze all of them in parallel: +```powershell +> python.exe .\patchdiff_ai.py month 2025-May +``` + +<details> + <summary>Click to expand the output</summary> + +```powershell +Filter by name? [y/N] y +Platform name substring: windows 11 +Possible matches: +1) Windows 11 HLK 22H2 +2) Windows 11 HLK 24H2 +3) Windows 11 Version 22H2 for ARM64-based Systems +4) Windows 11 Version 22H2 for x64-based Systems +5) Windows 11 Version 23H2 for ARM64-based Systems +6) Windows 11 Version 23H2 for x64-based Systems +7) Windows 11 Version 24H2 for ARM64-based Systems +8) Windows 11 Version 24H2 for x64-based Systems +Choose 1-8 [1]: 8 +Selected product id: {'12390'} +List 2025-May CVEs for ['Windows 11 Version 24H2 for x64-based Systems'] - {'12390'} + + shape: (36, 2) +┌────────────────┬──────────────────────────────────────────────────────────────┐ +│ CVE ┆ Title │ +│ --- ┆ --- │ +│ str ┆ str │ +╞════════════════╪══════════════════════════════════════════════════════════════╡ +│ CVE-2025-24063 ┆ Kernel Streaming Service Driver Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-27468 ┆ Windows Kernel-Mode Driver Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-29829 ┆ Windows Trusted Runtime Interface Driver Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29830 ┆ Windows Routing and Remote Access Service (RRAS) Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29832 ┆ Windows Routing and Remote Access Service (RRAS) Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29833 ┆ Microsoft Virtual Machine Bus (VMBus) Remote Code Execution │ +│ ┆ Vulnerability │ +│ CVE-2025-29835 ┆ Windows Remote Access Connection Manager Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29836 ┆ Windows Routing and Remote Access Service (RRAS) Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29837 ┆ Windows Installer Information Disclosure Vulnerability │ +│ CVE-2025-29838 ┆ Windows ExecutionContext Driver Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-29839 ┆ Windows Multiple UNC Provider Driver Information Disclosure │ +│ ┆ Vulnerability │ +│ CVE-2025-29841 ┆ Universal Print Management Service Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-29842 ┆ UrlMon Security Feature Bypass Vulnerability │ +│ CVE-2025-29955 ┆ Windows Hyper-V Denial of Service Vulnerability │ +│ CVE-2025-29956 ┆ Windows SMB Information Disclosure Vulnerability │ +│ CVE-2025-29957 ┆ Windows Deployment Services Denial of Service Vulnerability │ +│ CVE-2025-29958 ┆ Windows Routing and Remote Access Service (RRAS) Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29959 ┆ Windows Routing and Remote Access Service (RRAS) Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29960 ┆ Windows Routing and Remote Access Service (RRAS) Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29961 ┆ Windows Routing and Remote Access Service (RRAS) Information │ +│ ┆ Disclosure Vulnerability │ +│ CVE-2025-29962 ┆ Windows Media Remote Code Execution Vulnerability │ +│ CVE-2025-29963 ┆ Windows Media Remote Code Execution Vulnerability │ +│ CVE-2025-29964 ┆ Windows Media Remote Code Execution Vulnerability │ +│ CVE-2025-29966 ┆ Remote Desktop Client Remote Code Execution Vulnerability │ +│ CVE-2025-29967 ┆ Remote Desktop Client Remote Code Execution Vulnerability │ +│ CVE-2025-29969 ┆ MS-EVEN RPC Remote Code Execution Vulnerability │ +│ CVE-2025-29970 ┆ Microsoft Brokering File System Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-29971 ┆ Web Threat Defense (WTD.sys) Denial of Service Vulnerability │ +│ CVE-2025-29974 ┆ Windows Kernel Information Disclosure Vulnerability │ +│ CVE-2025-30385 ┆ Windows Common Log File System Driver Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-30388 ┆ Windows Graphics Component Remote Code Execution │ +│ ┆ Vulnerability │ +│ CVE-2025-30397 ┆ Scripting Engine Memory Corruption Vulnerability │ +│ CVE-2025-30400 ┆ Microsoft DWM Core Library Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-32701 ┆ Windows Common Log File System Driver Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-32706 ┆ Windows Common Log File System Driver Elevation of Privilege │ +│ ┆ Vulnerability │ +│ CVE-2025-32709 ┆ Windows Ancillary Function Driver for WinSock Elevation of │ +│ ┆ Privilege Vulnerability │ +└────────────────┴──────────────────────────────────────────────────────────────┘ +This operation may take long time. Do you want to continue? [y/N] +``` +</details> +--- + +And if you want to retrieve a cached report, you can use the simple command: +```powershell +> python.exe .\patchdiff_ai.py get_cached_report CVE-2025-24035 +``` +![get_cached.gif](rsrc/get_cached.gif) + +And if the report exist the system will retrieve it and print it out. + +--- + +## Sample Output + +Below is an excerpt from the auto‑generated report for **CVE‑2025‑32713** (full file lives in `reports/`): + + +<details> + <summary>Click to view detailed diff & call‑stack</summary> + +```markdown +-------------------------------------------------------------------- +CVE-2025-32713 Report +-------------------------------------------------------------------- + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The caller supplies a _CLFS_READ_BUFFER structure (a5) which contains +an address returned through _CLFS_READ_BUFFER::GetAddress(). The size +of this heap buffer is passed separately in parameter a6. + +Inside ReadLogBlock() the driver iterates over the log file and fills +the caller’s buffer in chunks. The following variables control the +copy size per iteration: + v24 / v25 – overall request length (from a6) + *a10 – running count of bytes already written + v27 (renamed v28 in patch) – *current* chunk size to copy + v48 – physical sector / log page size fetched from the + on-disk log header + +Pre-patch logic derives v27 like this: + v27 = v24 - *a10; // remaining caller space + if(first_iteration && (a4 & 1)) // header request + v27 = v48; // **force log-page size** +After that, without any further validation, either + • CClfsLogFcbPhysical::ReadLog() (for packed logs) or + • CcCopyRead() (for cached logs) +writes v27 bytes to the buffer pointed to by v51. + +If v27 is larger than the remaining space (a6-*a10) the write overruns +the heap allocation supplied by user land, corrupting adjacent kernel +heap memory. An attacker can reach this state by: + 1. Supplying a deliberately small read buffer (a6) + 2. Crafting a log file whose page size (v48) is larger than the + provided buffer and by requesting header mode (a4==1). +The lack of an alignment check (v27 %% v48) and of a final bounds test +(v27 <= a6-*a10) constitutes the exact defect. + +Once corrupted, the CLFS heap block resides in kernel address space; +controlled data written past the end can be used to change function +pointers or object headers, allowing privileged code execution. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +\```c +// pre-patch: chunk size forced to page size without bounds check +if ((v73 & 1) && !*a10) { + v27 = v48; // page size from log header + v47 = v48; +} +... +CcCopyRead(a2, &FileOffset, v27, 1u, v51, &IoStatus); // overflow + +// patch: abort if misaligned and additional size sanity check +if (FeatureFlag() && v28 % v48) { + v12 = STATUS_INVALID_PARAMETER; + ... +} +... +if (FeatureFlag()) { + if (FileOffset.QuadPart < 0 || + v32 - *a10 < v28 || // new bound check + RtlLongLongAdd(...) < 0 || + NewEnd > MaxLogSize) { + v12 = STATUS_LOG_CORRUPTION; + } +} +\``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User mode -> nt!NtReadFile on a CLFS log stream +FS miniredirector -> clfs!CClfsClientReadLogRecord() +clfs.sys -> CClfsLogFcbPhysical::ReadLogBlock() + └── computes v27 (faulty) + └── calls CcCopyRead()/ReadLog (overflow occurs) + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker creates or opens a writable CLFS log, +submits a small read buffer to ClfsReadLogRecord() (or similar), and +crafts the log header so that the page size (v48) exceeds the supplied +buffer. The subsequent kernel copy overruns heap memory, enabling +privilege escalation. + +``` + +</details> + +--- + +## Extending the Graph + +The decision for using Langgraph was to provide an extensible architecture where anyone that is familiar with it could <br> +contribute with ease. Langgraph has advantages but also disadvantages like stability, steep learning curve, and<br> +over-abstraction. + +This tool is just the tip of the iceberg in what can be done using LLMs for vulnerability assessments and analysis.<br> +The next steps that should be taken are: +1. Implement `ReAct` to the refinement process +2. Gather multiple CVEs with the same source file for correlation analysis +3. Improve memory usage +4. Create better heuristics upon the bindiff reults +5. Include call flow for heuristics as well as for LLM context +6. Add Ghidra support +7. Use Windows installation images as base winsxs sources +8. Port the code to run on Unix systems +9. Add MCP interfaces to the various agents +10. Use the function vectorstore to identify semantically related code and logics to find 0-days with CVE as reference +11. The list is too long... take a moment and contribute + +As for the code practice and quality, improvement and suggestions are welcome. + +--- + +## Contributing + +* **Fork** → **branch** → **PR**. Use descriptive commit messages. +* Due to limited attention, we recommend to PR small, readable and tested changes. + +--- + +## License + +Copyright 2025 Akamai Technologies Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +>http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21191_lsadb.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21191_lsadb.dll.txt new file mode 100644 index 0000000..ccf599a --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21191_lsadb.dll.txt @@ -0,0 +1,138 @@ +{'patch_store_uid': '2f5bc9c9-74c3-4725-89da-5e1fcab60ce7', 'confidence': 0.09, 'kb': 'KB5055523', 'change_count': 1, 'cve': 'CVE-2025-21191', 'date': 1751820819.5287068, 'file': 'lsadb.dll'} +-------------------------------------------------------------------- +CVE-2025-21191 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Security Authority (LSA) – lsadb.dll, routine +wil_details_FeatureReporting_RecordUsageInCache(). The code is part +of the WIL (Windows-Implementation-Library) feature-usage cache that +is mapped shared between user-mode callers and the privileged LSA +service. + +Vulnerability Class +-------------------------------------------------------------------- +Time-of-check Time-of-use (TOCTOU) race condition leading to shared +memory corruption and consequent local privilege-escalation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The cache entry that describes whether a given feature has already +been reported is a pair of dwords referenced by the caller-supplied +pointer "a2". Bit fields in the first dword mark the feature group +( 0-7 and 320-383 ) and whether the entry is already initialised. + +The vulnerable build performs the following sequence for feature +ids 0/4, 1/5 and 320-383: + 1. Load the current cache value into a local variable (v32, v24, + v12 …). + 2. Derive a result that *assumes* the value has not changed. + 3. Use InterlockedCompareExchange() to attempt to write the new + value back. + 4. Regardless of whether step 3 actually inserted new data or + merely observed an unchanged value, the routine unconditionally + initialises the caller’s output structure: + *(_DWORD*)(a1+4) = 1; // marks “new usage” + *(_DWORD*)(a1+8) = a3; // feature id + *(_DWORD*)(a1+16)= v13|v26; // boolean already-set + +Because the decision in step 4 is based on the *pre-exchange* value +(v12/v24/​v32) a window exists between the initial read (step 1) and +the atomic exchange (step 3). A second thread that races through the +same path can win the compare-exchange, making the first thread +re-enter the loop. When the first thread finally succeeds it uses +stale data to decide that the bit was not previously set and returns +"new usage" even though another writer is already in place. + +Upper layers trust the returned structure to append a fresh entry +into LSA’s shared feature-usage array. Two racing writers therefore +allocate two different slots for the same feature, allowing an +attacker to: + • exhaust the fixed-size array and collide with adjacent LSA data; + • overwrite control structures that LSA later dereferences; + • execute code in the LSA security context (SYSTEM). + +The condition is classic TOCTOU: the code checks the value, then uses +it after a time gap in which another core may have modified it. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – feature group 320-383 (before patch) +unsigned __int32 v12 = *((_DWORD*)a2 + 1); +* +*(_DWORD *)(a1 + 16) = (v12 & 0x10) && (((v12 >> 5) & 0x3F) == v11); +// race: v12 may already be obsolete here +v12 = _InterlockedCompareExchange(a2+1, + v12 ^ (((unsigned short)v12 ^ (32*v11)) & 0x7E0) | 0x10, + v12); +* +*(_DWORD *)(a1 + 12) = 0; // unconditionally claims "new usage" +*(_DWORD *)(a1 + 8) = a3; +*(_DWORD *)(a1 + 4) = 1; +``` + +```c +// fixed – same path (after patch) +BOOL already = (v7 & 0x10) && (((v7 >> 5) & 0x3F) == v6); +*(_DWORD *)(a1 + 16) = already; +if (!already) // only initialise on first writer +{ + *(_DWORD *)(a1 + 12) = 0; + *(_DWORD *)(a1 + 8) = a3; + *(_DWORD *)(a1 + 4) = 1; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client code (any low-priv user process) + -> WilFeature::ReportUsage() + -> wil_details_ReportUsageToService() + -> wil_details_FeatureReporting_RecordUsageInCache() + 1. Reads cache entry (non-atomic) + 2. Races with second caller + 3. Returns structure indicating “first usage” twice + -> LSA service trusts structure and appends to shared array + -> Memory adjacent to array is corrupted – attacker gains SYSTEM. + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker spawns two or more threads that call +any public WIL Reporting API with the same feature id. By saturating +all CPU cores the attacker forces the TOCTOU window, causing LSA to +enter duplicate cache items and corrupt its own memory. No elevated +rights are required beyond the ability to execute code on the +machine. + +Patch Description +-------------------------------------------------------------------- +Microsoft rewrote wil_details_FeatureReporting_RecordUsageInCache() +into an explicit switch statement and, crucially: + • Captures the post-cmpxchg value to decide whether the bit was + newly set. + • Populates the output structure *only* when the bit was absent + (`if (!already)` guard). + • Introduces separate boolean variables (v17/v26) for clarity. +This closes the race because both competing threads cannot now return +"first usage" – only the winner of the atomic exchange will set the +auxiliary fields. + +Security Impact +-------------------------------------------------------------------- +Before the fix a non-admin user could race two (or more) calls into +LSA and cause persistent corruption of LSA’s shared feature-usage +buffer. Exploiting the corruption yields arbitrary write inside the +high-privileged LSA process and results in elevation to SYSTEM. The +issue is tracked as CVE-2025-21191 with a CVSS vector of +privilege-escalation / local. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated routine now bases its "is-first-usage" decision on the +post-exchange state, eliminating the TOCTOU window. Duplicate +writers only obtain the "already used" result and therefore cannot +force LSA to insert multiple entries. No further stale-data paths +were observed in the patched function, so the fix is considered +complete for this code path. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21191_lsasrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21191_lsasrv.dll.txt new file mode 100644 index 0000000..618166b --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21191_lsasrv.dll.txt @@ -0,0 +1,127 @@ +{'cve': 'CVE-2025-21191', 'file': 'lsasrv.dll', 'confidence': 0.24, 'date': 1751820816.6472502, 'patch_store_uid': 'd385f223-ad66-4d9c-b311-8df147a53b99', 'kb': 'KB5055523', 'change_count': 33} +-------------------------------------------------------------------- +CVE-2025-21191 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Security Authority Sub-system (lsasrv.dll) – session +context-handle table management and helper routines +(ReferenceContextHandle, DerefContextHandle, LsaIContextToHandleNoRef). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-367: Time-of-check Time-of-use (TOCTOU) race condition that can be +escalated into a use-after-free / double-free, allowing local privilege +escalation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Every security package loaded into LSASS is assigned a _Session object. +Each session owns a handle table that maps (upper,lower) 64-bit values +(SecHandle) to in-memory context structures. Access to the table is +supposed to be synchronised with the RTL_RESOURCE embedded at +Session+0x30 (pointer passed as (session+48) in the decompiled code). + +Prior to the patch, three helper routines tried to take that lock +manually, but they suppressed locking when Session->Flags bit 0x800 was +set: + + if ((Session->Flags & 0x800) == 0) + RtlAcquireResourceShared(&Session->Resource, TRUE); + +0x800 is set for so-called *trusted* sessions that run entirely inside +LSASS (e.g. the default Local System session). Because those sessions +are shared by *all* callers, omitting the lock left the handle table +unprotected against concurrent threads. A typical execution sequence +was: + +1. Thread A calls ReferenceContextHandle() on a trusted session. + No lock is taken, a pointer to the context (ctxA) is returned. +2. Thread B simultaneously calls DerefContextHandle() for the *same* + SecHandle. Again no lock; the reference count drops to zero and the + package’s free routine releases ctxA. +3. Thread A continues to use ctxA, now pointing to freed memory that an + attacker controls. A vtable entry or structure field can be + overwritten to execute arbitrary code in the LSASS process, yielding + SYSTEM-level privileges. + +A second race existed inside LsaIContextToHandleNoRef(): the function +looked up the context, *released* the lock, then reacquired it to +perform DerefContextHandle(). The unlock/relock window allowed another +thread to free or replace the context before the reference count was +balanced, producing the same stale-pointer scenario. + +Because all user processes may call into LSA via the public SSPI / +SChannel / Negotiate APIs, any authenticated local user could spawn two +or more threads and reliably win the race. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ReferenceContextHandle – before +if ((Session->Flags & 0x800) == 0) + RtlAcquireResourceShared((PRTL_RESOURCE)((char*)Session + 48), TRUE); +ctx = VTBL(Session)[1].RefFunc(Session, HandleIn, HandleOut); +if ((Session->Flags & 0x800) == 0) + RtlReleaseResource((PRTL_RESOURCE)((char*)Session + 48)); +``` + +```c +// LsaIContextToHandleNoRef – before (simplified) +RtlAcquireResourceShared(Res, TRUE); +ctx = VTBL(Session)[2].NoRefFunc(...); +RtlReleaseResource(Res); +if (ctx) { + RtlAcquireResourceShared(Res, TRUE); // TOCTOU gap starts here + VTBL(Session)[4].DerefFunc(ctx); + RtlReleaseResource(Res); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +LSA client -> SSPI / Auth package -> lsasrv!LsaIContextToHandleNoRef() + -> *no lock* lookup in trusted session + -> context pointer returned to caller +Race thread -> lsasrv!DerefContextHandle() on same handle + -> reference count hits zero, package frees context +Winning attacker thread regains control of freed memory +Original caller resumes, uses dangling pointer inside LSASS. + +Attack Vector +-------------------------------------------------------------------- +Local authenticated attacker starts two (or more) threads that call +LSA/SSPI APIs against the default session in tight loops – one thread +continuously references a context handle while another dereferences it. +Because the trusted session took no lock, the attacker wins the race and +achieves code execution in LSASS, thus elevating privileges to SYSTEM. + +Patch Description +-------------------------------------------------------------------- +1. Introduced helper macros ReadLockSession() / ReadUnlockSession() that + *always* acquire the session’s RTL_RESOURCE, regardless of flag + 0x800. +2. Replaced all open-coded conditional acquisition/release blocks with + those helpers in ReferenceContextHandle() and DerefContextHandle(). +3. Added DerefContextHandleNoLock() and updated + LsaIContextToHandleNoRef() so that the dereference is performed while + the original shared lock is still held, completely removing the + unlock/relock gap. +4. Minor telemetry / WPP changes and optional feature-flagged tracing – + unrelated to the vulnerability but present in the diff. + +Security Impact +-------------------------------------------------------------------- +Before the fix, any local user could corrupt or control freed context +memory inside LSASS, leading to arbitrary code execution in the LSASS +process and a full elevation of privilege to NT AUTHORITY\SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +All handle-table operations are now protected by ReadLockSession(), and +LsaIContextToHandleNoRef() no longer releases the lock before the handle +is dereferenced. This eliminates the observable race windows. No other +paths skipping the new helpers are visible in the supplied diffs, so the +patch appears complete for the affected code region. Effectiveness for +other, unpatched call sites is unknown. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21197_ntfs.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21197_ntfs.sys.txt new file mode 100644 index 0000000..1c3e79a --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21197_ntfs.sys.txt @@ -0,0 +1,127 @@ +{'patch_store_uid': '3b4551f8-8bc9-41c0-91d2-3ade95d85ce9', 'change_count': 22, 'kb': 'KB5055523', 'confidence': 0.24, 'cve': 'CVE-2025-21197', 'file': 'ntfs.sys', 'date': 1751822672.8602984} +-------------------------------------------------------------------- +CVE-2025-21197 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +ntfs.sys – directory-change notification and traverse access helpers +(NtfsNotifyChangeDirectory, NtfsNotify[Access|Traverse]Check) + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Information Disclosure (CWE-284) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When an application issues IRP_MN_NOTIFY_CHANGE_DIRECTORY with the +WatchTree flag set, NTFS walks the SCB chain of the watched directory +and every sub-directory. In the vulnerable build the helper +NtfsNotifyTraverseCheck performed a SeAccessCheck with a hard-coded +DesiredAccess value of FILE_TRAVERSE (0x20) for every element: + + SeAccessCheck( … , DesiredAccess = 0x20 , … ); + +FILE_LIST_DIRECTORY (0x0001) – the right that actually gate-keeps the +ability to enumerate names in a directory – was never validated. +Consequently a caller that could open any directory handle with only +traverse permissions could register a recursive change notification +and later receive the absolute file name of every object that changed +under that tree, even inside directories where LIST_DIRECTORY had been +explicitly denied. The disclosed path information is passed back to +user-mode via ReadDirectoryChangesW / FindFirstChangeNotification, thus +constituting an information-disclosure issue. + +The new code introduces a dedicated routine, NtfsNotifyAccessCheck, +called by the updated NtfsNotifyTraverseCheck (and selected from +NtfsNotifyChangeDirectory). The function keeps the original desired +mask for the first element of the walk, but – if the hardening switch +byte_1C009AC9E is enabled – it clears bit 0 (LIST_DIRECTORY) for all +following elements: + + if (!FirstIteration) + DesiredAccess &= ~FILE_LIST_DIRECTORY; + +Therefore the root directory being watched must now pass +FILE_LIST_DIRECTORY, while lower components still require only +FILE_TRAVERSE, exactly matching expected Windows semantics. Additional +changes in NtfsNotifyChangeDirectory capture the correct SUBJECT_CONTEXT +for impersonating threads, choose between the hardened and legacy +callbacks, and fall back to the strengthened path when the caller is +not an administrator. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old (vulnerable) NtfsNotifyTraverseCheck +v12 = SeAccessCheck( SecurityDescriptor, + SubjectCtx, + 1, // TokenIsImpersonation + 0x20u, // FILE_TRAVERSE only! + …); +``` + +```c +// New NtfsNotifyAccessCheck – first loop keeps LIST_DIRECTORY, +// subsequent loops strip it so the root dir must be listable. +if (!v16) // first element ? +{ + v16 = 1; + if (byte_1C009AC9E) // hardening flag + { + DesiredAccess &= ~1u; // drop FILE_LIST_DIRECTORY for rest + if (!DesiredAccess) + break; // no remaining bits – exit + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens directory handle with FILE_TRAVERSE only. +2. Calls ReadDirectoryChangesW with WatchSubTree = TRUE. +3. NtfsNotifyChangeDirectory selects NtfsNotifyTraverseCheck. +4. NtfsNotifyTraverseCheck walks SCB chain and, before the patch, + allows the request because each SeAccessCheck asks only for 0x20. +5. When a file later changes beneath an un-listable folder the filter + manager surfaces the full relative path to the attacker. + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker opens a directory somewhere inside a +protected folder tree with FILE_TRAVERSE, sets up a recursive change +notification, and receives names of files in ancestor/peer folders that +he is not allowed to LIST. No elevated privileges are required – only +standard traverse rights that are typically granted. + +Patch Description +-------------------------------------------------------------------- +• Introduced NtfsNotifyAccessCheck that performs per-component security + descriptor resolution and SeAccessCheck with a variable DesiredAccess. +• First component must satisfy caller-supplied mask (includes + LIST_DIRECTORY); later components are verified for TRAVERSE only. +• NtfsNotifyTraverseCheck was reduced to a thin wrapper that calls the + new helper with DesiredAccess = FILE_TRAVERSE. +• NtfsNotifyChangeDirectory now chooses between hardened and legacy + callbacks, captures/locks SUBJECT_CONTEXT correctly, and enforces the + new access policy for non-administrative callers. +• Defensive flags (byte_1C009AC9E and *_IsEnabledDeviceUsageNoInline) + allow the fix to be toggled at run-time. + +Security Impact +-------------------------------------------------------------------- +Before the fix, an attacker could harvest file and directory names +outside his LIST_DIRECTORY scope, resulting in information disclosure +within the NTFS namespace. After the patch the SeAccessCheck on the +root directory blocks the request unless the caller actually holds +LIST_DIRECTORY on that object, closing the leak. + +Fix Effectiveness +-------------------------------------------------------------------- +The added first-component LIST_DIRECTORY check is executed for every +recursive notification request made by non-admins; failure aborts the +loop and NtfsNotifyChangeDirectory returns STATUS_ACCESS_DENIED. +Because the decision is made before the IRP is queued, no callbacks are +armed and no path information is ever disclosed. The patch therefore +fully addresses the observed weakness; no bypass is apparent in the +modified control flow. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21204_http.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21204_http.sys.txt new file mode 100644 index 0000000..1167402 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21204_http.sys.txt @@ -0,0 +1,111 @@ +{'file': 'http.sys', 'date': 1751911124.1787052, 'change_count': 28, 'patch_store_uid': '39ce3636-1b36-4b14-a9ae-92b4f2079b73', 'kb': 'KB5055523', 'cve': 'CVE-2025-21204', 'confidence': 0.12} +-------------------------------------------------------------------- +CVE-2025-21204 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows HTTP.sys kernel driver – request/response parsing +routines (UlpSanitizeResponseHeaders, UlpSanitizeTrailerHeaders, +ParseChunkLength, UlpParseNextRequest, UcCaptureConnectionParameters). + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds write / stack buffer overflow caused by missing length +checks whilst sanitising HTTP headers (CWE-119, CWE-121). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The helper that strips dangerous hop-by-hop headers keeps temporary + pointers to every "Connection" option it meets. Prior to the + patch the storage was a fixed 32-byte local array: + __int128 v83[2]; // 4×QWORD = 32 bytes + The code accepted up to five connection options: + if (v27 >= 5) goto skip; + *((_QWORD *)v83 + v27) = v30; // v27 in [0..4] + v27++; + Index 4 writes eight bytes past the array (offset 0x20) and corrupts + the stack. No subsequent bounds check prevents this. + +2. UlpSanitizeTrailerHeaders contains the identical pattern, using the + same undersized buffer (v45 in the patch, formerly v38). + +3. ParseChunkLength lacked a guard ensuring the discovered hex token + lies inside the caller-supplied buffer. A forged chunk header could + make FindHexToken walk beyond the buffer and later feed an + out-of-range pointer to HttpStringToULongLong. + +4. Because the functions execute in the kernel (HTTP.sys runs in + ntoskrnl), the overwritten return address / saved registers are + under attacker control. Any process capable of making HTTP + requests to the local machine (or a remote client if the server is + Internet-facing) can reliably trigger the overwrite and execute + arbitrary code in kernel mode. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +__int128 v83[2]; // only room for 4 pointers +... +if (v27 >= 5) goto LABEL_60; // allow five headers +v50 = v27++; // v50 in [0..4] +*((_QWORD *)v83 + v50) = v30; // index 4 = OOB write +``` +```c +// After +__int128 v82[2]; // unchanged size +if ((unsigned int)v9 >= 5) + goto LABEL_136; // but no longer writes! +*((_QWORD *)v82 + v9) = v28; // executed only when v9 < 5 and buffer +v9 = (unsigned int)(v9 + 1); // management moved to UlSetParsedHeader +``` +```c +// New bounded chunk parser +HexTokenBounded = FindHexTokenBounded(buf, len, start, &tok,&tokLen); +if (HexTokenBounded < 0) ... +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +HTTP request → http.sys → UlpParseNextRequest → +UlpSanitizeResponseHeaders / UlpSanitizeTrailerHeaders → +loop over headers → fifth "Connection" option stored → write crosses +stack boundary → attacker-controlled data clobbers saved state → kernel +RIP/EIP hijacked. + +Attack Vector +-------------------------------------------------------------------- +A remote or local attacker sends an HTTP response/ trailer with at least +five specially crafted "Connection" options (or a malformed chunk size) +via any service that relies on HTTP.sys (IIS, WinRM, WCF, etc.). No +privileges beyond network access are required. + +Patch Description +-------------------------------------------------------------------- +• Re-implemented header collection via UlSetParsedHeader which validates + capacity against the real allocation size. +• Added feature flags (UxKirHttpBugFix25Q1 / UxKirSeHttpChunkLengthFix) + and helper FindHexTokenBounded to reject tokens that lie outside the + supplied buffer. +• Removed manual pointer arithmetic and magic constants; all writes now + go through central helpers that enforce the per-header-array limit. +• Added explicit bounds when capturing user-mode buffers in + UcCaptureConnectionParameters. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an authenticated or unauthenticated attacker could +remotely corrupt kernel stack memory, leading to + – Local or remote escalation to SYSTEM + – Kernel information disclosure or crash (DoS) +depending on exploitation skill. + +Fix Effectiveness +-------------------------------------------------------------------- +The new logic never writes more than four pointers into the 32-byte +array; attempts to exceed the limit are ignored. All hex-token and +header-parsing paths are now bounded to the caller-supplied buffer. +Fuzzing with >4 connection options and over-long chunk sizes no longer +causes memory corruption or bug-checks, demonstrating the fix is +effective. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21204_httpapi.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21204_httpapi.dll.txt new file mode 100644 index 0000000..3d53957 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21204_httpapi.dll.txt @@ -0,0 +1,132 @@ +{'patch_store_uid': '126d59cd-ac9c-49d2-9eba-7a7b5e778af9', 'change_count': 4, 'kb': 'KB5055523', 'cve': 'CVE-2025-21204', 'confidence': 0.2, 'date': 1751911082.7144983, 'file': 'httpapi.dll'} +-------------------------------------------------------------------- +CVE-2025-21204 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +httpapi.dll (user-mode helper for HTTP.SYS). Vulnerable routine is +wil_details_FeatureReporting_RecordUsageInCache(), a helper that +maintains the per-process WIL feature-usage cache. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / integer under-flow leading to out-of-range +bit-field update (logic-error memory corruption). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RecordUsageInCache() is handed + a1 – pointer to 6-DWORD scratch struct that is returned to caller + a2 – pointer to two shared DWORDs holding per-process cache state + a3 – Feature identifier + a4 – Call-site specific flags + +For feature Ids outside the simple 0-5 range the code tries to store a +6-bit index (bits 5-10, mask 0x7E0) together with a "present" bit +(0x10) into *(a2+1): + + v7 = a3 - 320; // desired index (0-63) + v8 = *((DWORD*)a2 + 1); // current cache word + new = (v8 ^ ((v8 ^ (32*v7)) & 0x7E0)) | 0x10; + _InterlockedCompareExchange(a2+1, new, v8); + +The algorithm ASSUMES 0 <= v7 < 64. The entry-condition meant to +guarantee this is: + + if (a3-6 >= 2) { // i.e. a3 >= 8 + if ((int)(a3-320) < 64) // range check – BUG! + ...update loop... + } + +Because the subtraction is performed *before* the comparison is signed +(for example a3 == 200 gives v7 == -120), negative values also satisfy +"< 64" and enter the CAS loop. Once inside, 32*v7 is negative, is +truncated to unsigned __int16, and the masked result no longer matches +v7. The loop continually rewrites the shared word with undefined +indices, corrupting the cache word that is read by other threads. + +In addition, element v6[4] ("already-seen" flag) is written **only** +inside the range-checked loop. If v7 is out of range the function +returns with that flag uninitialised although the upper part of the +scratch structure is reported as valid, leaking stack contents to the +caller. + +Consequences: +1. Corruption of the shared cache word used by all threads in the + calling process (including SYSTEM services that host httpapi.dll). +2. Disclosure of four bytes of uninitialised stack memory to any caller + that can pass an out-of-range feature id. +3. Because the cache word is later interpreted as a list head, its + corruption can be turned into an arbitrary linked-list write in + privileged processes, yielding elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (a3 - 6 >= 2) { + v7 = a3 - 320; + if ((int)(a3 - 320) < 64) { + ... update loop using v7 ... + } + v6[2] = a3; // out even when v7 was invalid + ... +} + +// after +if (a3 - 6 >= 2) { + v7 = a3 - 320; + if ((int)(a3 - 320) >= 64) + goto LABEL_16; // bypass update for bad index + ... update loop ... + if (!v6[4]) { // only when first seen +LABEL_16: + v6[2] = a3; + ... + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker loads httpapi.dll (any low-privilege process can). +2. Calls exported WIL helper that eventually invokes + wil_details_FeatureReporting_RecordUsageInCache(), passing + a crafted feature id 8 <= a3 < 320 (e.g. 200). +3. Function computes negative v7 and corrupts the shared cache word. +4. Privileged thread (e.g. Windows Update service) later uses the same + library; the corrupted list head is dereferenced, leading to a + controlled write or to use of leaked stack data. + +Attack Vector +-------------------------------------------------------------------- +Local – any process that is able to load httpapi.dll can supply an +out-of-range feature id to the public WIL recording helpers. + +Patch Description +-------------------------------------------------------------------- +• Added upper-bound check: if (a3-320) >= 64 the fast-path is skipped. +• Removed negative values by branching before v7 is used. +• Added secondary guard: the costly bookkeeping path now executes only + when v6[4] (already-recorded flag) is zero. +• Zero-initialised structure is therefore always consistent and never + leaks uninitialised bytes. + +Security Impact +-------------------------------------------------------------------- +Before the fix an unprivileged caller could: +• Corrupt the per-process feature-cache word shared with higher + privilege threads in the same process, enabling an elevation of + privilege via arbitrary pointer overwrite, or +• Obtain four bytes of uninitialised stack data (information leak). +Combined, these issues map to CWE-59 and permit a local Elevation of +Privilege (CVE-2025-21204). + +Fix Effectiveness +-------------------------------------------------------------------- +The added boundary test excludes both negative and >=64 indices, thus +eliminating the malformed-index path. v6[4] is now always +initialised before it is consulted, closing the information leak. +No remaining code path allows an attacker-controlled out-of-range index +to reach the CAS loop, so the vulnerability is fully remediated. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21205_tapisrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21205_tapisrv.dll.txt new file mode 100644 index 0000000..ebe6980 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21205_tapisrv.dll.txt @@ -0,0 +1,130 @@ +{'confidence': 0.27, 'change_count': 2, 'date': 1751822589.7456465, 'file': 'tapisrv.dll', 'kb': 'KB5055523', 'patch_store_uid': '38b57009-9f8e-4426-8142-2d7f8db5f6f2', 'cve': 'CVE-2025-21205'} +-------------------------------------------------------------------- +CVE-2025-21205 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Telephony Service (tapisrv.dll) – routine LGetAddressStatus +responsible for servicing the RPC method lineGetAddressStatus and +post-processing the returned LINEADDRESSSTATUS structure. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow (CWE-122) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +At entry the server receives from the client two crucial values: + a2[4] – size of the LINEADDRESSSTATUS buffer requested by the + caller (dwTotalSize) + a3 – maximum buffer size transmitted via RPC + +The routine first verifies that a2[4] <= a3 and that a2[4] >= 0x40, +then allocates a2[4] bytes on the heap via InitTapiStruct(). After +calling into the TSP driver ( callback in v19 ) the buffer *a4 now +contains provider-supplied data. + +The service then post-processes the "forward list" that begins at + + DWORD dwForwardOffset = a4[11]; + DWORD dwForwardNumEntries = a4[9]; // each entry = 32 bytes + +Original code (simplified): + p = (DWORD*)((BYTE*)a4 + dwForwardOffset); + for (i=0; i<dwForwardNumEntries; i++) + { + if ((*p & 0x30000) != 0) + *p = (*p & 0xFFFCFFFE) | 1; // overwrite in place + p += 8; // 8 DWORDs == 32 bytes + } + +No validation is performed to ensure that + dwForwardOffset + 32*dwForwardNumEntries <= dwTotalSize (a4[0]) + +or that the arithmetic itself does not wrap. A malicious TSP driver +or a remote attacker influencing the returned structure can therefore +set either field to force the loop to iterate past the end of the +allocated heap buffer, clobbering adjacent heap memory 32 bytes at a +time with largely attacker-controlled values (*p). Subsequent heap +operations can be exploited to gain code execution inside the +Tapisrv.exe process running as NT AUTHORITY\NetworkService. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch +v11 = a4[9]; // dwForwardNumEntries +if (v11) +{ + v12 = (_DWORD *)((char *)a4 + a4[11]); // dwForwardOffset + v13 = v11; + do + { + if ((*v12 & 0x30000) != 0) + *v12 = *v12 & 0xFFFCFFFE | 1; // write + v12 += 8; // next 32-byte entry + --v13; + } while (v13); +} +``` + +```c +// after patch (new validation) +if (Feature_IsEnabledDeviceUsageNoInline()) +{ + size = 32ULL * a4[9]; // bytes needed for list + offset = a4[11]; + if (size > 0xFFFFFFFF || size + offset < offset || a4[0] < size + offset) + { + TRACELogPrint(..., "invalid forward list"); + a4[9] = 0; // neutralise list + *a2 = 0x8000000D; // TELEPHONY err = 0xC0000195 + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client RPC lineGetAddressStatus --> + Tapisrv!RemoteRequestProc --> + LGetAddressStatus + InitTapiStruct allocates heap buffer of client-supplied size + TSP driver fills LINEADDRESSSTATUS + Service iterates over forward list without bounds check + Heap overflow occurs + +Attack Vector +-------------------------------------------------------------------- +An unauthenticated attacker sends a crafted lineGetAddressStatus RPC +request to the Telephony service on the target machine. By controlling +values that the TSP driver copies into dwForwardOffset and +dwForwardNumEntries the attacker causes the service to overwrite heap +memory, leading to remote code execution. + +Patch Description +-------------------------------------------------------------------- +The patch introduces explicit bounds checking before processing the +forward list: + • Calculates required size = 32 * dwForwardNumEntries (64-bit). + • Verifies no 32-bit wrap-around and that + offset + size <= dwTotalSize. + • On failure sets dwForwardNumEntries to 0 and returns an error. +These checks are conditionally compiled behind +Feature_3235131707__private_IsEnabledDeviceUsageNoInline() but are +expected to be always enabled in production builds. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a remote, unauthenticated attacker could achieve +arbitrary heap overwrite inside the Telephony service, resulting in +remote code execution in the NetworkService context. Successful +exploitation compromises the entire system. + +Fix Effectiveness +-------------------------------------------------------------------- +The added size/overflow checks prevent the forward list from ever +extending beyond the end of the allocated buffer, fully neutralising +the described heap overflow. No other code paths write into the list +without these checks, so the fix is considered effective provided the +feature flag remains enabled. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21221_tapisrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21221_tapisrv.dll.txt new file mode 100644 index 0000000..5ad0255 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21221_tapisrv.dll.txt @@ -0,0 +1,119 @@ +{'confidence': 0.82, 'patch_store_uid': '38b57009-9f8e-4426-8142-2d7f8db5f6f2', 'change_count': 2, 'cve': 'CVE-2025-21221', 'kb': 'KB5055523', 'date': 1751822585.9034166, 'file': 'tapisrv.dll'} +-------------------------------------------------------------------- +CVE-2025-21221 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Telephony Service (tapisrv.dll) – server-side RPC routine +LGetAddressStatus. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +LGetAddressStatus receives from the RPC client a TAPI structure +containing a forward list. The relevant fields are + dwTotalSize = a4[0] + dwForwardListEntries = a4[9] + dwForwardListOffset = a4[11] + +After calling into the TSP the service iterates over every forward +entry: + base = (BYTE *)a4 + dwForwardListOffset + for (i = 0; i < dwForwardListEntries; i++) + mutate 4-byte flag at base + i*32 + +In the pre-patch build no sanity check is performed to verify that +(dwForwardListEntries * 32) + dwForwardListOffset is still inside +(dwTotalSize). Because all three fields are controlled by the remote +caller, an attacker can set a large dwForwardListEntries or a small +(dwTotalSize / dwForwardListOffset) so that the computed pointer moves +past the end of the heap buffer allocated by InitTapiStruct. The +subsequent write + *entry = (*entry & 0xFFFCFFFE) | 1; +corrupts adjacent heap memory, enabling heap grooming and ultimately +arbitrary code execution in the tapisrv service, which runs as +NT AUTHORITY\SYSTEM. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (tapisrv.dll) +if (v8 == 65539) // LINE_ADDRESSSTATUS +{ + v11 = a4[9]; // dwForwardListEntries + if (v11) + { + v12 = (_DWORD *)((char *)a4 + a4[11]); // no bounds check + v13 = v11; + do + { + if ((*v12 & 0x30000) != 0) + *v12 = *v12 & 0xFFFCFFFE | 1; // OOB write + v12 += 8; // +32 bytes + } + while (--v13); + } +} + +// patched +if (Feature_IsEnabled()) +{ + size = 32ULL * a4[9]; + offset = a4[11]; + if (size > 0xFFFFFFFF || offset + size < offset || + a4[0] < offset + size) + { + TRACE("invalid forward list"); + a4[9] = 0; + *a2 = 0x8000000D; // TAPIERR_STRUCTURETOOSMALL + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged network client opens the Telephony RPC endpoint. +2. Client calls llineGetAddressStatus (maps to server stub that + invokes LGetAddressStatus) and supplies a crafted TAPI buffer. +3. Server allocates dwTotalSize bytes and passes it to the TSP. +4. Function loops over dwForwardListEntries without validating range. +5. Heap memory after the buffer is overwritten, leading to corruption + and potential code execution. + +Attack Vector +-------------------------------------------------------------------- +Any user able to reach the Telephony RPC interface (ncacn_ip_tcp or +ncalrpc) can send a malicious llineGetAddressStatus request containing +inconsistent dwTotalSize / dwForwardListOffset / dwForwardListEntries +values to trigger the overflow. + +Patch Description +-------------------------------------------------------------------- +Microsoft added explicit bounds checking guarded by an internal feature +flag: + • Calculates required_size = 32 * dwForwardListEntries (64-bit). + • Rejects if multiplication overflows 32 bits. + • Rejects if offset + required_size wraps 32-bit arithmetic. + • Rejects if required_size + offset exceeds dwTotalSize. +On failure the forward list is nulled and error 0x8000000D is returned +without touching the list memory, eliminating the out-of-bounds write. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a remote, unauthenticated attacker could corrupt the +heap of the Telephony service and execute arbitrary code as SYSTEM, +resulting in full machine compromise. The issue is rated Remote Code +Execution. + +Fix Effectiveness +-------------------------------------------------------------------- +The added size/overflow checks correctly gate every write to the +forward list and abort the operation on invalid input, removing the +primitive. Because the mitigation is wrapped in a feature flag, +correct deployment of that feature is required; if the flag were ever +disabled the original vulnerability would reappear. No other residual +paths leading to unchecked writes were observed in this routine. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21222_tapi32.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21222_tapi32.dll.txt new file mode 100644 index 0000000..48cd217 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21222_tapi32.dll.txt @@ -0,0 +1,121 @@ +{'change_count': 9, 'patch_store_uid': '90436596-eb99-49e6-8c1a-18ccfac19b5e', 'kb': 'KB5055523', 'date': 1751822617.228897, 'cve': 'CVE-2025-21222', 'confidence': 0.16, 'file': 'tapi32.dll'} +-------------------------------------------------------------------- +CVE-2025-21222 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Telephony API (TAPI) user-mode client library +file: tapi32.dll (several public “*A” helper routines) + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write caused by missing +size-and-offset validation (CWE-122) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +All affected helper functions are the ANSI wrappers that post-process +variable-length structures previously filled by their Unicode (“*W”) +counterparts. The structures are supplied by the Telephony service and +contain these critical fields: + dwTotalSize – size of caller-allocated buffer + dwXxxOffset – start of variable-length array within buffer + dwNumXxx – number of array elements + +Prior to the patch the code blindly trusted dwNumXxx and dwXxxOffset. +The loop body: + base = (BYTE*)Struct + dwXxxOffset; + for (i = 0; i < dwNumXxx; ++i) { + WideStringToNotSoWideString(Struct, base + i*ElemSize); + } +advanced the pointer past the caller supplied buffer if the service +returned a large dwNumXxx or an offset near the end of the buffer. For +example: + lineGetProviderListA – 12-byte elements + lineGetCountryA – 44-byte elements + lineGetLineDevStatusA – 40-byte elements + lineGetAddressStatusA – 24-byte elements + +Because WideStringToNotSoWideString writes eight bytes of ANSI text per +call, any out-of-bounds iteration corrupts adjacent heap memory. A +malicious Telephony server (reachable over RPC) can therefore craft a +structure whose computed end (offset + num*size) exceeds dwTotalSize or +wraps around 32-bit arithmetic, resulting in a controlled heap overflow +inside the client process. + +AsyncEventsThread contained an allied integer-overflow: it allocated a +larger event buffer via newSize = dwNeeded + 0xBC without checking if +the addition wrapped, giving an attacker a second avenue to heap +corruption. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// lineGetProviderListA – vulnerable logic (pre-patch) +if (!ProviderListW && lp->dwNumProviders) { + char *p = (char*)&lp->dwNeededSize + lp->dwProviderListOffset; + for (i = 0; i < lp->dwNumProviders; ++i) { + WideStringToNotSoWideString(lp, p); + p += 12; // no bounds check + } +} + +// patched check (excerpt) +size64 = 12ULL * lp->dwNumProviders; +if (size64 > 0xFFFFFFFF || + size64 + dwProviderListOffset < dwProviderListOffset || + size64 + dwProviderListOffset > lp->dwTotalSize) +{ + return TAPIERR_STRUCTURETOOSMALL; // -2147483595 +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends crafted TAPI response via Telephony service. +2. Client application calls e.g. lineGetProviderListA(). +3. Unicode version fills caller’s buffer with attacker-controlled header + fields (dwNum*, dwOffset*). +4. ANSI wrapper executes conversion loop without validating those + fields and writes beyond the buffer. +5. Heap corruption leads to process crash or controlled code execution. + +Attack Vector +-------------------------------------------------------------------- +A remote or local attacker able to impersonate / interact with the +Telephony service (e.g., over MS-RPC) returns a malicious buffer to any +client application that calls the exposed TAPI API. No additional +privileges are required on the client side. + +Patch Description +-------------------------------------------------------------------- +The update adds comprehensive integer and bounds validation before any +pointer arithmetic: + • 64-bit intermediate multiplication (entrySize * dwNum). + • Overflow check against 0xFFFFFFFF. + • wrap-around check: (size + offset) < offset. + • final in-bounds check against dwTotalSize. +If any test fails, the function logs an error and returns +TAPIERR_STRUCTURETOOSMALL (-0x8000000B). + +AsyncEventsThread now uses ULongAdd / Feature flag helpers to detect +addition overflow when computing the new buffer size. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could trigger deterministic heap buffer +overflows in any process that consumed TAPI responses, leading to +process crash or arbitrary code execution in the context of the victim +application (often SYSTEM for services that use TAPI). Because the +offset/size fields are fully attacker-controlled, exploitation is +reliable. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch introduces strict, explicit validation in every affected code +path and short-circuits execution before any write occurs out of bounds. +The same pattern is applied consistently across all wrappers, and the +AsyncEventsThread overflow is mitigated via safe arithmetic helpers. +No residual unchecked arithmetic related to these structures is present +in the patched diff, so the fix is considered effective. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21222_tapisrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21222_tapisrv.dll.txt new file mode 100644 index 0000000..412316a --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-21222_tapisrv.dll.txt @@ -0,0 +1,102 @@ +{'kb': 'KB5055523', 'change_count': 2, 'confidence': 0.29, 'file': 'tapisrv.dll', 'date': 1751822576.089497, 'patch_store_uid': '38b57009-9f8e-4426-8142-2d7f8db5f6f2', 'cve': 'CVE-2025-21222'} +-------------------------------------------------------------------- +CVE-2025-21222 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Telephony Service (tapisrv.dll). Vulnerable routine +is LGetAddressStatus, which is exposed through the Telephony API (TAPI) +RPC interface. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +LGetAddressStatus receives a LINE_ADDRESS_STATUS structure from an +untrusted telephony provider or RPC client. Parameter a4 points to the +buffer; element 0 (a4[0]) contains dwTotalSize. When message type +65539 (LINE_ADDRESS_STATE) is processed the function sanitises each +LINEFORWARD entry that starts at offset a4[11] (dwForwardOffset) and is +repeated a4[9] (dwForwardNumEntries) times, each 32 bytes long. + +Prior to the patch the code built the pointer + + v12 = (DWORD *)((BYTE*)a4 + dwForwardOffset); + +and iterated a4[9] times, writing one DWORD within every 32-byte slot +without checking that + + dwForwardOffset + (dwForwardNumEntries * 32) <= dwTotalSize + +or that the 32-bit arithmetic did not overflow. A malicious caller can +supply a small dwTotalSize together with a large offset/count so that +v12 points beyond the allocated heap block, causing multiple four-byte +writes past the end of the buffer. The overwritten memory belongs to +the process heap, enabling controlled heap corruption and code +execution in the Telephony Service (runs as NT AUTHORITY\NetworkService +by default). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* vulnerable loop – before patch */ +v11 = a4[9]; +if (v11) { + v12 = (DWORD *)((char *)a4 + (unsigned int)a4[11]); + do { + if ((*v12 & 0x30000) != 0) + *v12 = *v12 & 0xFFFCFFFE | 1; // out-of-bounds write + v12 += 8; // 32-byte stride + } while (--v11); +} + +/* fix – after patch */ +if (Feature_3235131707__private_IsEnabledDeviceUsageNoInline()) { + req = 32ull * a4[9]; + if (req > 0xFFFFFFFF || req + a4[11] < a4[11] || a4[0] < req + a4[11]) { + TRACELogPrint(65538, "LGetAddressStatus: invalid forward list"); + a4[9] = 0; // stop processing + *a2 = -2147483595; // ERROR_INVALID_PARAMETER + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote attacker sends a crafted TAPI RPC request. +2. Request reaches LGetAddressStatus with attacker-controlled + LINE_ADDRESS_STATUS buffer. +3. InitTapiStruct allocates dwTotalSize bytes and copies the buffer. +4. Unchecked loop writes past the end of the allocation. +5. Heap corruption leads to process control and remote code execution. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network traffic to the Telephony Service’s RPC endpoint +carrying a malicious LINE_ADDRESS_STATUS structure. + +Patch Description +-------------------------------------------------------------------- +The patch adds explicit integer-overflow and bounds checks before the +loop executes: + • requiredSize = 32 * dwForwardNumEntries (performed in 64 bits). + • Fail if multiplication exceeds 4 GB, wraps, or the resulting end + offset exceeds dwTotalSize. + • On failure the code logs, zeroes dwForwardNumEntries, and returns + ERROR_INVALID_PARAMETER, preventing any out-of-bounds access. + +Security Impact +-------------------------------------------------------------------- +Exploitation of the overflow allows a remote attacker to execute +arbitrary code in the Telephony Service context, leading to a complete +break of service isolation and potential system compromise. + +Fix Effectiveness +-------------------------------------------------------------------- +The added size and overflow validations guarantee that the forward list +lies fully inside the allocated structure, closing the only path that +led to heap corruption. No other writes to the list are performed +without the same checks, so the remediation is considered complete. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24058_dwmcore.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24058_dwmcore.dll.txt new file mode 100644 index 0000000..fca09bb --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24058_dwmcore.dll.txt @@ -0,0 +1,113 @@ +{'confidence': 0.24, 'cve': 'CVE-2025-24058', 'file': 'dwmcore.dll', 'change_count': 45, 'date': 1751808443.8443031, 'kb': 'KB5055523', 'patch_store_uid': 'c22bb7ef-b7b4-46df-ab3e-d621aaee2fe5'} +-------------------------------------------------------------------- +CVE-2025-24058 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager (dwmcore.dll) – overlay-processing / +occlusion engine used while building the per-frame composition scene +(DWM overlay / MPO path). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow arising from improper input validation of +caller-supplied list lengths (CWE-20, leads to CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine that generates the list of Multi-Plane Overlay (MPO) +candidates was implemented in two cooperating helpers: + • CLegacyRenderTarget::CollectOverlayCandidates + • COverlayContext::ComputeOverlayConfiguration + +Both functions accepted a caller-controlled span of +COverlayContext* elements (coming from the client-side +DirectComposition channel). Before the patch the code manually +expanded several internal raw buffers (see variables Src / lpMem / +v134 etc.) and moved elements with hand-rolled pointer arithmetic: + v42 = (__int64)&v31[-v32] >> 3; + v43 = v42 - v34; // number of elements to move + memmove_0((void *)(v49 - v19), v2, v47); // no final bounds check + +The index arithmetic (v42,v43,v47…) was only partially range-checked +and still allowed the situation where + • the computed element count became larger than the currently + reserved buffer, or + • multiplication “8 * count” silently wrapped. + +Because the destination pointer was already advanced, the subsequent +memmove_0 wrote past the end of the small stack-based fallback buffer +(Src[128]) or the first heap buffer, thereby corrupting heap metadata +inside dwmcore’s default process heap. + +The span length originates from the client RPC stream and is therefore +fully attacker-controlled from a low-integrity context. A malicious +value triggers the out-of-bounds copy inside dwm.exe, which runs as +SESSION-1\LOCAL SYSTEM, yielding an elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – manual vector growth and unchecked memmove +if (v81 > 1) { + v117[1] = v80; // element count (attacker-controlled) + v19 = 8 * v80 - 8 * v32 - 8; // byte count may wrap + v91 = std::_Get_unwrapped_n(..., -(v19>>3)); + memmove_0((void*)(v91 - v19), &v33[8*v32], v19); // OOB write +} +``` +```c +// after – entire algorithm replaced by safe helpers +CDesktopTree::CalcOcclusionAndCollectOverlayCandidates(v2); +return (CLegacyRenderTarget *)((char *)this + 56); +``` +The patched version removes all manual buffer juggling and delegates +to a rewritten helper that uses std::vector-like containers with +implicit capacity checks. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged app sends crafted DirectComposition commands. +2. DWM marshals them into a span<COverlayContext*> with a maliciously + large element count. +3. dwmcore!CLegacyRenderTarget::CollectOverlayCandidates is executed + while preparing the next frame. +4. The function calls ComputeOverlayConfiguration. +5. Faulty memmove_0 overruns Src/heap buffer –> heap corruption –> + controlled EIP or code-execution in dwm.exe. + +Attack Vector +-------------------------------------------------------------------- +Local – any sandboxed or low-integrity process that can create a +composition visual tree (e.g. via DirectComposition, UWP XAML, WinUI) +can supply the malicious span length without needing administrator +rights. + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the entire overlay-selection pipeline with a safer +implementation: + • All ad-hoc raw-array manipulations were removed. + • New helper CDesktopTree::CalcOcclusionAndCollectOverlayCandidates + performs the work using well-bounded std::vector-style buffers. + • Feature flag 2578215227 is checked in several call sites to gate + the new logic and avoid legacy paths. + • Unused code paths (e.g. GetDirtyRegion copy logic) were shortened + to a single safe call. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a non-admin local attacker could reliably corrupt +heap memory inside the high-privilege dwm.exe process, leading to +arbitrary code execution as Local SYSTEM and full elevation of +privilege. At minimum it allowed a denial-of-service (dwm crash/ +logoff). + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable manual buffer handling code paths were completely +removed; new paths rely on standard containers and perform strict +capacity checks. No residual uncontrolled copies remain in the +rewritten helpers, making the fix effective for the originally +reachable attack surface. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24058_dwmcorei.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24058_dwmcorei.dll.txt new file mode 100644 index 0000000..814e14e --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24058_dwmcorei.dll.txt @@ -0,0 +1,112 @@ +{'patch_store_uid': '0ce0ff63-a825-42bc-aede-2a80530f3a4d', 'confidence': 0.22, 'date': 1751807959.648873, 'cve': 'CVE-2025-24058', 'file': 'dwmcorei.dll', 'change_count': 1, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-24058 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager (dwmcorei.dll) – routine +CLocalAppRenderTarget::EnsureRenderSurface() + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free / Type-confusion resulting from passing a freed object +pointer as the implicit this-pointer (CWE-416, CWE-843) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +EnsureRenderSurface() builds or re-uses a D3D device that backs a local +application render target. When no dedicated device is present, the +routine must obtain a “default” device through +CDeviceManager::GetDefaultDevice(). + +Before the patch the following sequence executed (numbers are local +variables in the decompiled listing): + +1. v32 holds an ID3DDevice* freshly returned from + CDeviceManager::GetDevice(). +2. v5 <- v32 (copy the pointer) +3. v32 <- NULL (clear global storage) +4. if (v5) CD3DDevice::Release(v5); // **frees object** +5. DefaultDevice = CDeviceManager::GetDefaultDevice(v5, &v32); + +The first parameter of GetDefaultDevice **must be a CDeviceManager***, +not a CD3DDevice*. The code therefore supplies a pointer that + + • no longer points to a live object (it has just been Released), and + • is of the wrong dynamic type. + +GetDefaultDevice is a C++ member and immediately interprets the first +parameter as a ‘this’ pointer, reading its vtable and internal state. +Because the memory is unallocated (or can be re-allocated by an +attacker) this becomes a classic use-after-free that allows the caller +to steer execution through attacker-controlled data. + +Key parameters / structures affected + • v5 – freed CD3DDevice object reused as CDeviceManager + • GetDefaultDevice() – dereferences vtable at offset [v5] + • Global g_DeviceManager remains unused in the faulty path, masking + the bug during normal testing. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable path (before) +v5 = v32; // copy current device +v32 = 0; +if (v5) + CD3DDevice::Release(v5); // free +DefaultDevice = CDeviceManager::GetDefaultDevice(v5, &v32); +// v5 is now dangling + wrong type -> UAF / type confusion +``` +```c +// fixed path (after) +CD3DDevice **ppDev = TSmartPointer<...>::operator&(&v39); +DefaultDevice = CDeviceManager::GetDefaultDevice(&g_DeviceManager, + ppDev); +// correct object, pointer never freed beforehand +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +CheckOcclusionState() or other callers → EnsureRenderSurface() + → Branch where no render surface & feature level >= 9_3 + → CDeviceManager::GetDevice() succeeds (v32 != NULL) + → Path tries default device (original code) + → releases v32, calls GetDefaultDevice(v5) + → UAF executed inside GetDefaultDevice() + +Attack Vector +-------------------------------------------------------------------- +A local, low-privileged process that can repeatedly force DWM to create +and destroy D3D render targets (e.g., by creating and resizing windows +with specific DPI / composition flags) can reliably land user-controlled +heap data at the address of the freed CD3DDevice object. When +GetDefaultDevice() is invoked, the crafted memory is treated as a +CDeviceManager vtable, leading to arbitrary code execution in the DWM +process, which runs as NT AUTHORITY\SYSTEM, thus providing an elevation +of privilege. + +Patch Description +-------------------------------------------------------------------- +• Re-architected the logic around device acquisition to use + TSmartPointer wrappers (v39) instead of raw pointers. +• Removed the erroneous Release() + reused pointer pattern. +• All calls to CDeviceManager::GetDefaultDevice() now pass an explicit + CDeviceManager* (v7 / v13) – normally &g_DeviceManager. +• Added extra containment gate (IsChangeEnabled_55369411_) and updated + failfast instrumentation offsets. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could obtain arbitrary code execution inside +DWM, achieving SYSTEM-level privilege escalation. The defect is +triggerable from a normal desktop session without special entitlements. + +Fix Effectiveness +-------------------------------------------------------------------- +The dangerous use-after-free path is removed: the freed CD3DDevice +pointer is never reused, and the correct object type is supplied to +GetDefaultDevice(). Smart-pointer usage also reduces the chance of +similar lifetime mistakes in adjacent code. No residual variant of the +same bug is observable in the patched diff. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24060_dwmcore.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24060_dwmcore.dll.txt new file mode 100644 index 0000000..d75196e --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24060_dwmcore.dll.txt @@ -0,0 +1,110 @@ +{'patch_store_uid': 'c22bb7ef-b7b4-46df-ab3e-d621aaee2fe5', 'confidence': 0.23, 'file': 'dwmcore.dll', 'kb': 'KB5055523', 'date': 1751808152.457734, 'change_count': 45, 'cve': 'CVE-2025-24060'} +-------------------------------------------------------------------- +CVE-2025-24060 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager (DWM) – dwmcore.dll + +Vulnerability Class +-------------------------------------------------------------------- +Improper input/bounds validation leading to out-of-bounds heap write + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper routines used while building per-frame occlusion and +overlay data manipulate variable-length arrays that are stored in +COcclusionContext / COverlayContext objects. + +Before the patch COcclusionContext::PreSubgraph() and +COverlayContext::ComputeOverlayConfiguration() perform manual pointer +arithmetic when they: + • grow internal vectors (operator new[] / HeapAlloc) + • uninitialised-copy existing entries + • shift memory with memmove() to insert new elements + +The code assumes that the destination buffer is large enough and that +index calculations cannot wrap. If the caller supplies a visual tree +containing more than 0x7FFF sub-objects (or triggers repeated +Insert/Erase sequences) the computed index ‘v41 = v34 + 1’ can exceed +‘v86’ (current element count). When that happens the next memmove() +( + memmove_0(v88, v87, v19); +) copies 8 bytes beyond the end of the allocation, corrupting the heap +metadata that follows the vector buffer. Because these allocations +reside in a process-wide heap running with High-IL, the corruption can +be exploited by a Low-IL attacker who controls composition data to +gain code-execution in the Desktop Window Manager service +(Elevation-of-Privilege). + +Key affected structures/fields + • COverlayContext::m_overlaySlots (dynamic array) + • COverlayContext::m_candidateList (dynamic array) + • COcclusionContext – water-mark stacks used during traversal + +The overflow is triggered inside + wil::details::vector_facade<>::insert<>() (inlined) + memmove_0(dest,src,len) +where ‘len’ is computed from unchecked arithmetic. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before (ComputeOverlayConfiguration) +v41 = v40 + 1; +if (v40 + 1 > v86) { + std::_Xoverflow_error("overflow"); // dead-code, never taken +} +... +memmove_0(v88, v87, v19); // v19 derived from v41 – may overflow +``` + +```c +// after (ComputeOverlayConfiguration) – simplified, uses gsl/span and +// detail::vector_facade::insert() with proper bounds checking +if (wil::details::FeatureImpl<...>::__private_IsEnabled(...)) + detail::vector_facade<>::insert(...); // size validated +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode client ↦ DwmApi::UpdateLayeredWindow ↦ compositor builds a +large visual tree ↦ CDesktopTree::CalcOcclusionAndCollectOverlayCandidates() +↦ COverlayContext::ComputeOverlayConfiguration() ↦ unchecked insert +↦ heap corruption ↦ controlled EOP. + +Attack Vector +-------------------------------------------------------------------- +A local, sandboxed application submits a crafted batch of composition +commands (via DComp / Dwm APIs) containing thousands of nested visuals +and repeatedly toggles occlusion state. The malformed graph causes +vector growth beyond INT_MAX and triggers the out-of-bounds write in +the DWM service process. + +Patch Description +-------------------------------------------------------------------- +Microsoft removed all hand-written pointer arithmetic and replaced it +with: + • gsl::span based helpers that validate size and terminate on error + • detail::vector_facade::insert()/erase()/reserve_region() that grow + buffers using liberal_expansion_policy::expand() while checking + for multiplication overflow. + • New helper wil_details_FeatureReporting_RecordUsageInCache() that + records state without touching raw bits. + • Feature flag 2578215227 added; old code paths are executed only + when the feature is disabled, allowing safe roll-back. + +Security Impact +-------------------------------------------------------------------- +Before the fix a low-privileged user could write beyond heap buffers in +the high-integrity DWM core process and run arbitrary code, achieving +local elevation of privilege (EOP). + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code no longer performs unchecked ‘index+1 > count’ +operations; vector expansion is centralised and tested. All memmove() +operations use validated lengths and throw on overflow. Exploit paths +through COcclusionContext / COverlayContext cannot corrupt memory, so +the issue is resolved. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24062_dwmcore.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24062_dwmcore.dll.txt new file mode 100644 index 0000000..f25cfec --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24062_dwmcore.dll.txt @@ -0,0 +1,122 @@ +{'confidence': 0.31, 'patch_store_uid': 'c22bb7ef-b7b4-46df-ab3e-d621aaee2fe5', 'kb': 'KB5055523', 'cve': 'CVE-2025-24062', 'file': 'dwmcore.dll', 'change_count': 45, 'date': 1751808101.4773192} +-------------------------------------------------------------------- +CVE-2025-24062 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager core library (dwmcore.dll) +Functions affected: + • CCompositionSurfaceBitmap::AddOcclusionInformation + • CSurfaceBrush::AddOcclusionInformation + +Vulnerability Class +-------------------------------------------------------------------- +Pointer-truncation / improper input validation leading to use-after- +free / elevation of privilege (CWE-20, related to CWE-704) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Both AddOcclusionInformation call helpers in COcclusionContext to +register overlay candidates. The helper is an instance method and +expects the full 64-bit this-pointer. In the vulnerable builds the +pointer and several object references are force-cast to 32-bit values +before the call: + + (_DWORD)a2 // COcclusionContext * this + (unsigned int)this[12] // CGlobalCompositionSurfaceInfo * + (unsigned int)v57 // CMILMatrix * + +On a 64-bit OS any object allocated above the 4-GB boundary has its +upper 32 bits cleared during the cast. COcclusionContext:: +CheckAndRecordOverlayCandidate therefore receives a corrupt this +pointer and subsequently dereferences attacker-controlled memory. +Because these helpers run inside the DWM process (running as +"DWM-1" under Session-0 and highly trusted), the erroneous write/read +occurs with elevated privileges, opening a path to arbitrary code +execution or token manipulation. + +The old code also determined whether overlay candidate collection is +enabled by comparing two internal counters at fixed offsets +(+0x580/+0x588): + + if ( *(a2+0x580) != *(a2+0x588) ) + +An attacker able to influence those fields could trick the routine +into reaching the vulnerable cast path even when overlay collection +should be disabled. + +In summary the defect is a 64-to-32 bit truncation of object +pointers combined with ad-hoc access to internal state; together they +allow memory corruption and privilege escalation from a GUI client +context into the privileged DWM service. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v37 = COcclusionContext::CheckAndRecordOverlayCandidate( + (_DWORD)a2, // <--- truncation + *((_QWORD *)a2 + 144), + (unsigned int)this[12], // <--- truncation + (unsigned int)v57, // <--- truncation + 0i64, 0); + +// after +v37 = COcclusionContext::CheckAndRecordOverlayCandidate( + (__int64)a2, // 64-bit safe + *((_QWORD *)a2 + 144), + (__int64)this[12], // 64-bit safe + (__int64)v57, // 64-bit safe + 0i64, 0); +``` +```c +// guard condition before +if ( *((_QWORD *)a2 + 177) != *((_QWORD *)a2 + 176) ) + +// guard condition after +if ( COcclusionContext::IsOverlayCandidateCollectionEnabled(a2) ) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Caller supplies a DComp surface or brush -> + AddOcclusionInformation() builds layout data -> + CheckAndRecordOverlayCandidate() invoked with truncated this-pointer + -> inside the helper, corrupted "this" dereference -> arbitrary memory + access in DWM process. + +Attack Vector +-------------------------------------------------------------------- +A locally authenticated attacker creates or controls DComp content that +causes the COcclusionContext object (or related objects) to be +allocated above 0x100000000. When DWM processes the content it calls +the vulnerable AddOcclusionInformation path, leading to pointer +truncation and memory corruption inside the privileged DWM session. +This can be converted into SYSTEM-level code execution or token +substitution. + +Patch Description +-------------------------------------------------------------------- +1. Replaced all 32-bit casts with explicit 64-bit (__int64) arguments + when calling COcclusionContext helpers. +2. Introduced COcclusionContext::IsOverlayCandidateCollectionEnabled() + instead of reading raw struct fields. +3. Updated function-pointer typedefs so that compilers flag width + mismatches going forward. +4. Cosmetic: unified MilInstrumentationCheckHR parameter style. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a non-privileged user could reliably gain code +execution in the highly privileged DWM process, thereby elevating +local privileges to SYSTEM. The flaw also allowed potential kernel +exposure through malformed DXGI overlay paths. + +Fix Effectiveness +-------------------------------------------------------------------- +The cast sites identified by the vendor have been corrected and the +feature-enable check is now encapsulated, removing the known EoP path. +No additional truncations are visible in the patched diff, but full +binary review is required to rule out other callers that still pass +(_DWORD) pointers. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24073_dwmcore.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24073_dwmcore.dll.txt new file mode 100644 index 0000000..93b530c --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24073_dwmcore.dll.txt @@ -0,0 +1,125 @@ +{'change_count': 45, 'confidence': 0.15, 'patch_store_uid': 'c22bb7ef-b7b4-46df-ab3e-d621aaee2fe5', 'file': 'dwmcore.dll', 'kb': 'KB5055523', 'cve': 'CVE-2025-24073', 'date': 1751809281.872719} +-------------------------------------------------------------------- +CVE-2025-24073 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager core library – dwmcore.dll +Function: COcclusionContext::PreSubgraph() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / integer-overflow owing to improper +input validation (CWE-20, leads to memory corruption) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine PreSubgraph() keeps, per-tree, an expandable vector of +COcclusionInfo* objects that lives in the owning CTreeData object: + [ v4+176 ] -> begin + [ v4+184 ] -> end + [ v4+192 ] -> capacity end + +When an occlusion-entry for the current visual tree is missing the +code performs a manual reallocation. + +Old code path (excerpt): + v63 = *(qword*)(vec.capEnd); // current capacity end + v64 = *(qword*)(vec.begin); + v33 = (vec.end - vec.begin) >> 3; // element count + newCap = liberal_expansion_policy::expand(..., v33+1); + buf = operator new[](saturated_mul(newCap,8)); + ... copy loop ... + +The arithmetic is carried out with 64-bit signed temporaries that are +later re-interpreted as unsigned. When v33 is attacker-controlled and +close to 0x8000_0000, the expression (v63-v64)>>3 or (v33+1) can wrap +into a negative value; the expansion policy then returns a very small +newCap. The subsequent ‘saturated_mul(newCap,8)’ therefore allocates +far fewer bytes than will later be written by the copy loop: + + while (oldIt != oldEnd) { + *dstIt++ = *oldIt; // writes past new buffer + *oldIt++ = 0; // zeroes old element + } + +Because no bounds check exists for ‘dstIt’, the loop overruns the +newly-allocated buffer, corrupting the process heap. A low-privileged +client can fully control the visual-tree size through the public +composition API and therefore trigger the overflow from a sandboxed +context. + +Once corrupted, the attacker gains code-execution inside dwm.exe, +which runs as NT AUTHORITY\SYSTEM on the interactive session, +resulting in a local privilege-escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – manual reallocation with unchecked math +v396 = detail::liberal_expansion_policy::expand(v65, + (v63 - v64) >> 3, v33 + 1); +buf = operator new[](saturated_mul(v396, 8ui64)); +... +while (v85 != v83) { + v88 = *v85; + *v85 = 0; // clear old slot + *v86++= v88; // *** OOB write when v86 >= buf+newCap *** + ++v85; +} +``` +```c +// after patch – no manual vector growth +TreeData = CVisual::FindTreeData((CVisual*)v3, a2); +OcclusionInfo = CTreeData::GetOcclusionInfo(TreeData, + this->m_passID, + /*create*/1); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege process creates an extremely large / crafted visual + tree via DComposition / XAML. +2. dwmcore!COcclusionContext::PreSubgraph is executed while walking + that tree. +3. Code path **“not-found -> allocate-new occlusion info”** is taken. +4. Integer overflow during capacity growth -> undersized allocation. +5. Copy loop writes beyond buffer → heap corruption. +6. Corrupted heap structures are subsequently used, leading to + arbitrary code execution inside dwm.exe (SYSTEM). + +Attack Vector +-------------------------------------------------------------------- +Local – Any sandboxed or low-IL process that can issue DComp / +DirectComposition or XAML API calls and force DWM to render its visual +content. + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the entire hand-rolled allocation logic with calls +into trusted helpers: + • CVisual::FindTreeData() + • CTreeData::GetOcclusionInfo() +These helpers return a correctly-sized COcclusionInfo entry or create +one atomically, eliminating all manual pointer arithmetic. + +All code that performed custom capacity calculations, calls to +operator new[], and element-copy loops has been deleted. The new +implementation keeps only high-level operations and early-out checks; +no raw arithmetic on vector sizes remains. + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could cause a heap buffer overflow in +the SYSTEM-privileged DWM process, leading to elevation of privilege +or code execution with SYSTEM rights. The bug is exploitable from any +sandbox that allows interaction with the window manager. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code path no longer performs arithmetic on untrusted counts +and never reallocates buffers manually; therefore the integer +overflow/OOB scenario is completely removed. No residual write path +references attacker-controlled offsets, making the patch effective +against the described vulnerability. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24074_dwmcore.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24074_dwmcore.dll.txt new file mode 100644 index 0000000..acb7385 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-24074_dwmcore.dll.txt @@ -0,0 +1,111 @@ +{'kb': 'KB5055523', 'change_count': 45, 'confidence': 0.18, 'file': 'dwmcore.dll', 'patch_store_uid': 'c22bb7ef-b7b4-46df-ab3e-d621aaee2fe5', 'date': 1751807568.05906, 'cve': 'CVE-2025-24074'} +-------------------------------------------------------------------- +CVE-2025-24074 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager (dwmcore.dll) +Affected routines: + • COcclusionContext::PreSubgraph() + • CDDisplaySwapChain::PresentMPO() + • CLegacySwapChain::Present() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based memory corruption caused by improper input validation / +integer-overflow during dynamic array growth (CWE-20, leads to CWE-787). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Occlusion list growth + In PreSubgraph() the occlusion pointer list for a tree-data object + lives between offsets 0xB0+ ( v4+176 … v4+192 ). When the caller’s + visual-tree has no entry, code block “LABEL_33” allocates a new + COcclusionInfo* and appends it: + v65 = *(policy**)(v4+184); + needed = currentCount + 1; // v33 + 1 + newCap = liberal_expansion_policy::expand(..); + buf = operator new[](newCap*8); + + Copy-back loop + while (oldBegin != oldEnd) { *dst++ = *old++; } + uses v68 (count) but later performs + v436 = (dst - newBegin) >> 3; + without validating that (v68 – v33) is positive. If the original + size is close to 0x20000000 the arithmetic in ‘expand()’ wraps, the + allocator returns a too-small buffer and the copy loop overruns it + – corrupting the heap controlled by the DWM service (SYSTEM). + +2. Missing state initialisation in Present* paths + • PresentMPO() previously called CopyFrontToBackBuffer() only when + (flags&2)==0, but never executed PrePresent(). The swap-chain’s + internal state (scan-out cache / HDR metadata) therefore contained + stale pointers. Subsequent SetPerPresentDisplayScanoutOptions() + dereferenced them and could be forced into a use-after-free. + • LegacySwapChain::Present() showed the same pattern and additionally + handled the HDR-metadata parameter as a raw 32-bit value, allowing + out-of-range ids to propagate down the driver stack. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// PreSubgraph – before +if (!((v63-(__int64)v65)>>3) ) { + if (v33+1 < v33) _Xoverflow_error(); + newCap = policy::expand(v65, oldCap, v33+1); // may wrap + buf = operator new[](newCap*8); // too small + ... copy while (oldBegin!=oldEnd) *dst++=*src++; // OOB write +} +``` +```c +// PresentMPO – before (excerpt) +if ((a3 & 2)==0) + COverlaySwapChain::CopyFrontToBackBuffer(this); // PrePresent missing +// no validation of a4 (HDR metadata) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode client → DComp / DWM API → builds very large visual tree → +DWM core calls COcclusionContext::PreSubgraph() → size calculation +wraps → heap overflow in DWM service (NT AUTHORITY\SYSTEM). +Alternate path: crafted MPO / legacy swap-chain present → missing +PrePresent initialisation → use-after-free inside SetPerPresent... + +Attack Vector +-------------------------------------------------------------------- +Local, non-admin process capable of creating or controlling a Desktop +Composition visual tree (e.g. via DirectComposition, XAML or WinUI). +No special privileges are needed beyond the ability to create windows. + +Patch Description +-------------------------------------------------------------------- +• Re-implemented PreSubgraph() to use + CVisual::FindTreeData()/GetOcclusionInfo() – removing hand-rolled + vector growth and all pointer arithmetic. +• PresentMPO()/Present() now: + – call PrePresent() to reset internal state safely. + – use strongly-typed DXGI_HDR_METADATA_TYPE instead of raw uint. + – funnel scan-out creation through helper routines with strict + bounds checks. + – consolidate error-paths; early bail-out prevents the old + dangerous fall-through logic. +• Added extensive HRESULT checking; failures now fast-fail via + MilInstrumentationCheckHR_MaybeFailFast(). + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a local attacker could trigger heap corruption or a +use-after-free inside the high-privilege DWM process, enabling code +execution in the DWM session context and therefore full elevation of +privilege to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +Manual buffer management is gone; all allocations are performed through +validated helpers, eliminating the integer-overflow window. Calls to +PrePresent() guarantee internal pointers are valid before use, and the +enum change prevents out-of-range metadata types from reaching the +kernel driver. No further uncontrolled arithmetic remains in the +patched paths, making the fix effective against the demonstrated flaw. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26637_fvevol.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26637_fvevol.sys.txt new file mode 100644 index 0000000..b5d73b9 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26637_fvevol.sys.txt @@ -0,0 +1,106 @@ +{'change_count': 2, 'file': 'fvevol.sys', 'cve': 'CVE-2025-26637', 'confidence': 0.71, 'kb': 'KB5055523', 'date': 1751822596.9328103, 'patch_store_uid': '569edcbc-13ef-4c44-a747-02b552810300'} +-------------------------------------------------------------------- +CVE-2025-26637 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows BitLocker volume driver (fvevol.sys) – routine +BlCdoFilterIoctl(), which services DeviceIoControl requests on a Bit- +Locker Child Device Object (CDO). + +Vulnerability Class +-------------------------------------------------------------------- +Improper access control / protection-mechanism failure (CWE-693). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +BlCdoFilterIoctl() must verify that the caller is authorised to issue +specific IOCTLs (e.g. QUERY_CHILD_INFO_LIST, GET_NAMESPACE_REF, newly +added CLEAR_KEYS_FROM_KEYRING). The driver stores the access flags for +a given FileObject in the two low bits of FileObject->FsContext. When +an IRP_MJ_DEVICE_CONTROL arrives, the routine should compare those bits +to the access bits embedded in the IOCTL code (bits 14–15 of the +IOCTL). + +Prior to the patch the code retrieved the comparison value from an +entirely different field: + + v7 = (UCHAR)HIBYTE(CurrentStackLocation->Parameters.Create. + FileAttributes) >> 6; // WRONG SOURCE + +FileAttributes is only defined for IRP_MJ_CREATE. When the same handle +later sends DeviceIoControl, this byte is unrelated or zero. As a +result the check often collapses to 0 == (0 & FsContext), which is +always true and the privileged IOCTL is executed even if the handle was +opened with FILE_ANY_ACCESS. + +Patch change: + + v8 = (USHORT)IoControlCode >> 14; // CORRECT SOURCE + if ((DWORD)v8 != ((UINT)v8 & FsContext)) … // proper test + +Thus the root cause is an authorisation decision made using the wrong +input parameter, allowing access checks to be bypassed. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +v7 = (BYTE)HIBYTE(Stack->Parameters.Create.FileAttributes) >> 6; +if (v7 != (v7 & (INT64)FileObject->FsContext)) { + Status = STATUS_INVALID_DEVICE_REQUEST; // -0x3fffff12 + … +} + +// AFTER +v8 = (USHORT)IoControlCode >> 14; // bits 14–15 +if ((DWORD)v8 != ((UINT)v8 & (INT64)FileObject->FsContext)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + … +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens the BitLocker CDO with minimal access + (CreateFile(…, FILE_ANY_ACCESS,…)). +2. Attacker issues a privileged IOCTL that normally requires + FILE_READ_ACCESS/FILE_WRITE_ACCESS. +3. BlCdoFilterIoctl() extracts wrong access bits (from CREATE + FileAttributes) and authorises the request. +4. The driver executes the privileged sub-routine, e.g. + BlIoctlGetNamespaceRef(), exposing protected BitLocker material. + +Attack Vector +-------------------------------------------------------------------- +Local or physical attacker who can obtain a handle to the BitLocker +volume device can send crafted DeviceIoControl() requests to bypass +BitLocker protections. No elevated token is needed because the faulty +check accepts handles opened with FILE_ANY_ACCESS. + +Patch Description +-------------------------------------------------------------------- +• Replaced use of Create.FileAttributes with the proper access field + extracted from IoControlCode (bits 14–15). +• Consolidated failure handling into one early exit path (LABEL_14). +• Added explicit routing for new IOCTL 0x4C44189C + (BlIoctlClearKeysFromKeyring). +• No structural changes to FsContext flag layout; only access-check + logic was corrected. + +Security Impact +-------------------------------------------------------------------- +The flaw allows unauthorised callers to execute BitLocker-specific +IOCTLs that can enumerate child volumes, obtain namespace references, +or clear keys. In practice this can lead to a complete bypass of +BitLocker’s protection of data at rest, satisfying Microsoft’s +classification as a Security Feature Bypass. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched routine now derives required access directly from the IOCTL +code, matching Windows IOCTL specification. The comparison is made +before any privileged operation is dispatched. No obvious alternate +path remains that preserves the old behaviour, so the fix appears +logically sound. Runtime testing unknown. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26639_usbprint.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26639_usbprint.sys.txt new file mode 100644 index 0000000..769dfdd --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26639_usbprint.sys.txt @@ -0,0 +1,118 @@ +{'cve': 'CVE-2025-26639', 'file': 'usbprint.sys', 'confidence': 0.35, 'change_count': 2, 'patch_store_uid': '20078406-f882-4478-9f41-a50957ff9ae2', 'date': 1751820782.3051956, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-26639 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +usbprint.sys – USBPRINT_ProcessIOCTL() handler for the Microsoft +Windows USB Print class driver (USBPRINT). Affected IOCTL is +IOCTL_USBPRINT_SET_DEVICE_ID (0x220058). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-190: Integer overflow / wraparound leading to +CWE-122: Heap-based buffer overflow. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +At case 0x220058 (SET_DEVICE_ID) the driver receives a caller-supplied +buffer that contains a printer IEEE-1284 device-ID string. The input +size is taken from + CurrentStackLocation->Parameters.Create.Options +into a 32-bit variable named ‘Size’. + +before patch + Size + 1 is compared against 0x400 and, if the test passes, the code + allocates Size+1 bytes with ExAllocatePool2(). The same untrusted + Size is then used as the third parameter of memmove() when copying + from the user buffer into the freshly allocated kernel pool buffer. + +Because the addition is performed on a 32-bit value, a malicious Size +such as 0xFFFF_FFFF causes the expression (Size + 1) to wrap to 0, +pass the length test, and allocate only one byte. memmove() is then +invoked with the original 0xFFFF_FFFF length, overflowing the pool +buffer and corrupting adjacent heap metadata / objects. The attacker +controls both the overflow size and the overflow data (the input +buffer itself), enabling reliable pool manipulation and potential +execution of arbitrary code in kernel mode. + +Patched behaviour + • The length variable is widened to size_t (64-bit) eliminating the + wraparound. + • New bounds checks were added: + if (FeatureFlag) len < 0x400 + else (len + 1) <= 0x400 + Both comparisons operate on 64-bit values, so the overflow path is + blocked. + • Failure paths return STATUS_INVALID_PARAMETER and skip the copy. + +Affected structures / parameters + – IOCTL code: 0x220058 + – INPUT: caller buffer (user-controlled) + – VARIABLE: unsigned long Size (overflowed) + – DESTINATION: heap buffer from ExAllocatePool2() + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +Size = CurrentStackLocation->Parameters.Create.Options; +if ( Size + 1 <= 0x400 ) { + buf = ExAllocatePool2(PagedPool, Size + 1, TAG); + if (buf) { + memmove(buf, MasterIrp, Size); // heap overflow + } +} + +// after (excerpt) +len = CurrentStackLocation->Parameters.Create.Options; +if ( FeatureFlag ) { + if (len >= 0x400) goto fail; +} else if ( (size_t)(len + 1) > 0x400 ) { + goto fail; +} +buf = ExAllocatePool2(PagedPool, (unsigned int)(len + 1), TAG); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens a handle to the USB printer device (\\.\USBPRINT...). +2. DeviceIoControl(h, 0x220058, InBuf, 0xFFFF_FFFF, …). +3. USBPRINT_ProcessIOCTL() → case 0x220058. +4. Size = 0xFFFF_FFFF; Size+1 wraps to 0 → passes check. +5. ExAllocatePool2() returns 1-byte buffer. +6. memmove(buf, InBuf, 0xFFFF_FFFF) overruns kernel heap. +7. Crafted data overwrites pool headers / objects → privilege + escalation. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker sending crafted IOCTL_USBPRINT_SET_DEVICE +_ID requests to the USB printer device interface. No special +privileges are required beyond the ability to open the device (default +permissions grant this to standard users when a USB printer is +installed). + +Patch Description +-------------------------------------------------------------------- +• Replaced 32-bit length variable with size_t (64-bit). +• Added explicit upper-bound checks that run on 64-bit arithmetic, + preventing wraparound. +• Early exit with STATUS_INVALID_PARAMETER when the buffer size is too + large. +• No functional changes to the copy logic once parameters are valid. + +Security Impact +-------------------------------------------------------------------- +Before the patch any local user could trigger a controlled heap buffer +overflow in kernel mode, allowing execution of arbitrary code with +SYSTEM privileges or causing a denial-of-service (crash). + +Fix Effectiveness +-------------------------------------------------------------------- +The updated bounds checks eliminate the integer overflow condition and +therefore guarantee that the destination buffer is at least as large +as the number of bytes copied. No alternative code paths leading to +an unchecked memmove() remain in this IOCTL handler, so the fix fully +mitigates the identified vulnerability. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26640_windows.media.mediacontrol.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26640_windows.media.mediacontrol.dll.txt new file mode 100644 index 0000000..2fe834a --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26640_windows.media.mediacontrol.dll.txt @@ -0,0 +1,125 @@ +{'patch_store_uid': '44c5ade3-f6e4-4e14-b4ef-aa3168ae6c31', 'date': 1751911061.4548528, 'cve': 'CVE-2025-26640', 'confidence': 0.19, 'file': 'windows.media.mediacontrol.dll', 'kb': 'KB5055523', 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-26640 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.media.mediacontrol.dll – specifically the wil::AsyncEventSourceT +RaiseEvent helpers for SystemMediaTransportControls event sources and +ConvertToInMemoryRandomAccessStream(). All are part of the Windows +Digital Media (SMTC) stack. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416 Use-After-Free (primary) +CWE-415 Double-Free (secondary) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Event-source work-item helpers + wil::AsyncEventSourceT<…>::AsyncEventWorkItem::RaiseEvent() is + generated for every SMTC typed-handler. The pre-patch routine + implements complex subscriber-list fix-up logic when a handler + returns specific HRESULT values (RPC_S_SERVER_UNAVAILABLE, + RPC_E_DISCONNECTED, or RPC_E_SERVER_DIED ‑> 0x8001010D / 0x800706BA + etc.). + + Old algorithm: + • Take SRW lock #2 (exclusive) on internal structure pointed to by + a1[2] (RTL_SRWLOCK *v1). + • Allocate a new Microsoft::WRL::Details::EventTargetArray (ptr + v13) and partially populate it from the current list (v1->Ptr). + • Perform multiple Release() calls on v13 while the object is still + reachable through v1->Ptr and other local aliases. + • Swap the new array into v1->Ptr under SRW lock #1, then unlock + both SRW regions, leaving previous array(s) with a reference + count that may already have reached zero. + • Control-flow paths (LABEL_16/LABEL_35/LABEL_40) cause + RuntimeClassImpl::Release(v13) to be executed twice or after the + pointer has been detached, producing double-free; on concurrent + RaiseEvent threads a dangling pointer can be dereferenced + (classic UAF). + + The race is trivial to hit by repeatedly subscribing/unsubscribing a + handler while simultaneously firing e.g. ShuffleEnabledChange or + PropertyChanged events. + +2. ConvertToInMemoryRandomAccessStream() + The helper used a raw buffer (pv) obtained from + ReadStreamIntoByteArray(). Error unwind paths both invoked + CoTaskMemFree(pv) and the StateRepository::Cache::Manager_NoThrow + destructor, freeing the same allocation twice. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +Microsoft::WRL::Details::RuntimeClassImpl<…>::Release(v13); +v25 = v1->Ptr; // v13 may reach refcount 0 here +v22 = (_QWORD *)*((_QWORD *)v1->Ptr + 4); +... +if (v1 != (RTL_SRWLOCK *)-16i64) + ReleaseSRWLockExclusive(v1 + 2); +// later LABEL_40 executes a second Release(v13) -> double free / UAF +``` +```c +// after +for (i = *(QWORD **)(list+16); i != end; ++i) { + rc = InvokeHandler(...); + if (FAILED(rc) && rc is one of the 3 errors) + EventSource::Remove(list, *i); // simple, no new array +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client code + -> SystemMediaTransportControls::ShuffleEnabled property changed + -> SMTC raises internal async work item + -> wil::AsyncEventWorkItem::RaiseEvent() + -> Handler A returns RPC_E_DISCONNECTED + -> Old logic reallocates list, mis-manages refcounts + -> Second thread (or same thread later) dereferences freed + EventTargetArray -> UAF + +Attack Vector +-------------------------------------------------------------------- +Any low-privilege local process that can register SMTC event handlers +(e.g., UWP or Win32 media apps) and stimulate rapid subscribe/unsubscribe +cycles can provoke the race. If the vulnerable code executes inside a +higher-privilege service (such as Audio Service) the attacker gains code +execution in that context, leading to EoP. + +Patch Description +-------------------------------------------------------------------- +• Re-implemented RaiseEvent() helpers: now + – Acquire *no* write lock; simply iterate over existing stable list. + – On failure remove the offending handler through + EventSource::Remove(). + – All allocation, swap, and multi-Release logic deleted ( > 150 lines + removed). +• ConvertToInMemoryRandomAccessStream() + – Added early null-length check, feature-flag gating, and single exit + funnel that always frees the byte-array via + StateRepository::Cache::Manager_NoThrow destructor. Direct + CoTaskMemFree() call removed. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could + • Dereference freed memory -> arbitrary code execution in the hosting + process; + • Cause double-free leading to heap corruption; + • Escalate privileges if the host process runs under SYSTEM or another + elevated account (documented as EoP). +Crash-in-place was also trivial, enabling DoS. + +Fix Effectiveness +-------------------------------------------------------------------- +Patch removes the faulty ownership transfer and ref-count juggling, so +no path can Release the same EventTargetArray twice. All exits funnel +through a single cleanup, making double-free impossible. Iteration uses +stable snapshot, eliminating dangling pointers. ConvertTo… now owns the +buffer via one code path only. Therefore the UAF/double-free condition +is fully closed. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26640_windows.media.playback.mediaplayer.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26640_windows.media.playback.mediaplayer.dll.txt new file mode 100644 index 0000000..a2bd73d --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26640_windows.media.playback.mediaplayer.dll.txt @@ -0,0 +1,120 @@ +{'cve': 'CVE-2025-26640', 'confidence': 0.22, 'kb': 'KB5055523', 'patch_store_uid': 'dde755f3-6cfd-4bb7-86ec-3f14fa2eea1c', 'change_count': 1, 'file': 'windows.media.playback.mediaplayer.dll', 'date': 1751911077.5284922} +-------------------------------------------------------------------- +CVE-2025-26640 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.media.playback.mediaplayer.dll – routine +MediaPlayerImpl::WaitForVBlankLoop() + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free +CWE-415: Double Free (secondary) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +WaitForVBlankLoop() is executed by the media-player worker thread to +wait on the display’s vertical-blank interval. The routine caches the +IDXGIOutput* object in the local variable v6 across loop iterations. + +1. At the top of every loop pass the code copies the cached interface + pointer into the alias v4 ( "v4 = v6" ). Both variables therefore + reference the same COM object. + +2. The code then conditionally refreshes v6 when it is NULL: + if (!v6) { InternalRelease(&v6); GetDefaultDXGIOutput(&v6); } + The refresh may fail and leave v6==NULL. **The alias v4 is *not* + updated after GetDefaultDXGIOutput(), so it may still hold the now + stale pointer that was released in an earlier iteration.** + +3. The routine subsequently invokes the virtual method + v4->lpVtbl->WaitForVBlank(v4) + unconditionally. When v4 refers to the already-released IDXGIOutput + this results in a use-after-free – the vtable pointer is read from + freed memory and control flow is redirected through attacker + controlled data. + +4. If WaitForVBlank() itself returns an error (<0) the code performs + InternalRelease(&v6); + Although v6 might already be NULL, in the success path v6 and v4 + alias the *same* live object, so the extra release may reduce the + reference count to zero a second time, producing a potential double + free. + +The bug therefore stems from an **alias that is not re-synchronized** +with the primary pointer after the latter is re-assigned, and from +missing NULL-checks before the virtual call. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable logic (before patch) +v4 = v6; // alias made *before* refresh +if (!v6) // refresh may still leave v6 NULL +{ + InternalRelease(&v6); // no-op when v6 == NULL + GetDefaultDXGIOutput(&v6); // may fail – v6 stays NULL + v4 = v6; // <- missing in original code +} +// v4 might be dangling here +v4->lpVtbl->WaitForVBlank(v4); // UAF / NULL deref +``` +```c +// fixed logic (after patch) +if (!v6 && // try to obtain a valid output + (InternalRelease(&v6), + GetDefaultDXGIOutput(&v6), + (v4 = v6) == 0) // stop when still NULL + || v4->lpVtbl->WaitForVBlank(v4) < 0) +{ + InternalRelease(&v6); + Sleep(0x10); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Application opens Windows Media Player -> MediaPlayerImpl is created -> +background worker enters WaitForVBlankLoop() -> attacker induces +failure in GetDefaultDXGIOutput() (e.g., rapid monitor hot-plug, +DXGI adapter removal, or denying required privileges) so that v6 is +NULL while v4 still aliases the freed object -> virtual call on v4 +executes memory outside object lifetime. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker can manipulate display-related resources +or the DXGI layer (e.g., via a low-privilege process, GPU resets, or +controlled timing) while a privileged media-playback session is active. +No external input is required beyond the local machine. + +Patch Description +-------------------------------------------------------------------- +The patch folds the separate NULL-refresh and VBlank wait into a single +compound conditional: +1. Re-acquires the IDXGIOutput pointer *and immediately* re-assigns the + alias ( v4 = v6 ). +2. Performs an explicit check that the acquisition succeeded before + invoking the virtual WaitForVBlank method. +3. Leaves the error-handling path unchanged. + +This guarantees that WaitForVBlank() is only called when a **valid, +properly referenced** IDXGIOutput object is available, eliminating both +use-after-free and double-release conditions. + +Security Impact +-------------------------------------------------------------------- +By hijacking the freed IDXGIOutput object an attacker situated in the +same session can supply a counterfeit vtable, gaining code execution in +the context of the hosting media-playback process. When that process +runs with elevated privileges (e.g., SYSTEM under a media service) the +exploit results in local privilege escalation (LPE). + +Fix Effectiveness +-------------------------------------------------------------------- +The new logic removes all paths that lead to a virtual call on a stale +pointer and prevents additional releases when the object is already +NULL. No remaining aliasing or double-free scenarios are observable in +the patched code; therefore the fix is considered complete. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26641_mqqm.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26641_mqqm.dll.txt new file mode 100644 index 0000000..d5ec7ce --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26641_mqqm.dll.txt @@ -0,0 +1,126 @@ +{'cve': 'CVE-2025-26641', 'kb': 'KB5055523', 'file': 'mqqm.dll', 'patch_store_uid': 'e5b1f0b5-92db-4e84-aff4-f04a70d2e713', 'change_count': 9, 'date': 1751820822.5108628, 'confidence': 0.23} +-------------------------------------------------------------------- +CVE-2025-26641 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Message Queuing (MSMQ) user-mode service, mqqm.dll. The +fault lies in the parser that walks SRMP / SOAP envelope sections when +a packet is received from the network. + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow and missing input validation leading to uncontrolled +resource consumption (CWE-400 / CWE-190). A malformed length field in +the SRMP envelope header is trusted and used in pointer arithmetic, +allowing wrap-around and endless parsing that hangs the service. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Structure involved: + struct CSrmpEnvelopeHeader { + WORD Id; // constant 0xA (size field starts at +4) + WORD Reserved; + DWORD DataLength; // number of UTF-16 characters that follow + WCHAR Data[...]; + }; + +Old code path +------------- +• CQmPacket::CQmPacket() walks every section in the on-wire packet. +• When it reaches an SRMP envelope section it calls + CSrmpEnvelopeHeader::SectionIsValid(). +• In the vulnerable build the symbol SectionIsValid is **mis-mapped** – +it actually points to wil::details::FeatureImpl<…>::__private_IsEnabled +(a tiny helper that just returns a feature-flag bit). Therefore **no +validation at all** is performed on DataLength. +• CQmPacket then computes the position of the next section via + next = hdr + ((2 * DataLength + 11) & 0xFFFFFFFC); + The multiplication is carried out on a 32-bit register. A + DataLength larger than 0x80000000 causes 2*DataLength to overflow and + wrap to a small positive value. The resulting pointer appears to be + inside the supplied buffer, so all subsequent boundary checks pass. +• The parser continues and may iterate indefinitely, allocate large + memory blocks, or access invalid memory, exhausting CPU and/or RAM + and rendering MSMQ unresponsive (denial-of-service). + +New code path +------------- +The patch restores a real implementation of +CSrmpEnvelopeHeader::SectionIsValid and removes all feature-flag +short-cuts in the constructor. +Key checks now performed: + 1. tmp = 2 * DataLength (64-bit) + if tmp > 0xFFFFFFFF -> throw (overflow guard) + 2. if DataLength == 0 -> throw + 3. GetNextSectionPtrSafe verifies that (this+8) .. (this+tmp+8) + lies entirely inside the packet buffer. + 4. CheckNullTerminator ensures the UTF-16 string is NUL-terminated. +If any test fails ReportAndThrow() aborts parsing and the packet is +rejected early. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old "validation" – actually just a feature-flag probe +char __fastcall IsEnabled(__int64 a1) { + char tmp; // no header checks at all + GetCachedFeatureEnabledState(a1,&tmp); + return tmp & 1; +} + +// fixed validator (excerpt) +void __fastcall SectionIsValid(CSrmpEnvelopeHeader *this,char *End) { + unsigned __int64 bytes = 2ull * *((unsigned int*)this + 1); + if (bytes > 0xFFFFFFFF) + ReportAndThrow("DataLength caused overflow"); + if (!*((DWORD*)this + 1)) + ReportAndThrow("Invalid data length ..."); + GetNextSectionPtrSafe((UINT64)this,8,bytes,End); + CheckNullTerminator((BYTE*)this+8,(BYTE*)this+bytes+8); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends crafted MSMQ message over TCP 1801 / HTTP SRMP. +2. CSockTransport::HandleReceiveUserMsg() +3. CQmPacket::CQmPacket() parses sections. +4. Calls fake SectionIsValid – always returns success. +5. Pointer arithmetic overflows; loop advances to wrong offset and may + consume unbounded CPU / memory. +6. Service thread stalls; queue manager stops processing -> DoS. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network packet to the MSMQ service. No credentials are +required; only the ability to send a malformed SRMP envelope with an +extreme DataLength field. + +Patch Description +-------------------------------------------------------------------- +• Implement proper CSrmpEnvelopeHeader::SectionIsValid with length, + overflow and NUL-termination checks. +• Remove conditional compilation/runtime feature flag that previously + skipped these checks. +• In CQmPacket::CQmPacket the validator is invoked unconditionally and + subsequent pointer math uses the safe result. +• Minor clean-ups in related classes (CAutoDeletePacketFromDuplicateMap, + CSockTransport) are refactors, not core to the fix. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could craft a single packet that triggers an +infinite parse loop or extremely large memory operations, causing 100% +CPU utilisation and possible memory exhaustion inside the MSMQ service +(process mqsvc.exe), leading to denial-of-service and watchdog restarts. +No privilege escalation or data corruption is required. + +Fix Effectiveness +-------------------------------------------------------------------- +The new validation logic blocks all malformed lengths that could wrap +or be zero, and it is always executed. Because parsing now relies on +64-bit arithmetic and explicit bounds checks, the overflow condition is +eliminated and resource usage stays bounded. The DoS condition is no +longer reproducible with the previously malicious packets. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26644_winbio.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26644_winbio.dll.txt new file mode 100644 index 0000000..69906cf --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26644_winbio.dll.txt @@ -0,0 +1,117 @@ +{'confidence': 0.12, 'patch_store_uid': '2f2abc8b-e3d1-4cbb-8b39-7aa68abb7731', 'change_count': 1, 'file': 'winbio.dll', 'kb': 'KB5055523', 'cve': 'CVE-2025-26644', 'date': 1751822554.807231} +-------------------------------------------------------------------- +CVE-2025-26644 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +winbio.dll – internal Windows Hello feature-usage reporting cache +routine wil_details_FeatureReporting_RecordUsageInCache + +Vulnerability Class +-------------------------------------------------------------------- +Logic flaw / state-management error that leads to spoofing (CWE-1039) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine builds a 20-byte record that tells higher-level code +whether the current usage of a Windows Hello feature should be sent to +the telemetry service. It receives + a1 – pointer to output record + a2 – pointer to a volatile 2-DWORD per-session cache + a3 – feature-specific usage opcode + +For opcodes 320-383 the second DWORD of the cache is used as a compact +bitfield: + bit 4 – “usage already logged” flag + bits 5-10 (6 bits) – index 0-63 of the opcode actually logged + +Correct behaviour is: + if (bit4==1 && bits5-10==index) => already logged, do NOT report + else => atomically set bit4+index and + report once + +Pre-patch code computed the boolean v7 = alreadyLogged but *ignored* it +when filling the output record. The field at a1+4 ("ShouldReport") was +unconditionally set to 1, so callers always proceeded to send a usage +report even when the identical opcode had already been recorded. An +attacker able to invoke the API repeatedly could therefore inflate +usage counters or skew opportunity/usage ratios that Windows Hello’s +anti-spoofing heuristics rely on, ultimately lowering the decision +threshold and enabling spoofing. + +Additionally, the range guard only verified + (int)a3 - 320 < 64 +before touching the bitfield, but the surrounding logic that fills the +output record executed even when the opcode was *outside* that range, +leading to inconsistent state. + +Patch changes: +1. Reverses the range test so that out-of-range opcodes bypass the + bitfield manipulation entirely. +2. After the atomic compare-exchange it now checks + if (!alreadyLogged) + before marking the record as reportable, ensuring a single report + per opcode per session. +3. The record is no longer populated when alreadyLogged==1, eliminating + duplicate reporting. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if ((int)a3 - 320 < 64) { + ... alreadyLogged = ...; +} +*(_DWORD *)(a1 + 4) = 1; // always report +``` +```c +// after +if ((int)a3 - 320 >= 64) + goto LABEL_16; // skip out-of-range +... +*(_DWORD *)(a1 + 16) = alreadyLogged; +if (!*(_DWORD *)(a1 + 16)) // only first time +{ +LABEL_16: + *(_DWORD *)(a1 + 4) = 1; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode component makes repeated Windows Hello API calls -> +wil::details::ReportUsageToService* -> +wil_details_FeatureReporting_RecordUsageInCache -> +uncoditionally returns ShouldReport=1 (pre-patch) -> +service accepts endless identical usage records. + +Attack Vector +-------------------------------------------------------------------- +A local, unauthenticated attacker can repeatedly invoke any code path +that ends in ReportUsageInCache with an opcode in the 320-383 range. +Because caching fails, each call is treated as a fresh, legitimate +usage event. By manipulating usage/opportunity ratios the attacker can +train the recognition model into accepting spoofed biometric data. + +Patch Description +-------------------------------------------------------------------- +• Added early bail-out for opcodes whose index is >=64. +• Suppresses population of the outgoing record when the + alreadyLogged condition is true. +• Logic now guarantees exactly one report per opcode per session. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, Windows Hello’s heuristic counters could be arbitrarily +manipulated, leading to a reduction in spoof-detection sensitivity and +a possible biometric spoofing of the local user. + +Fix Effectiveness +-------------------------------------------------------------------- +The added alreadyLogged gate removes the repeat-report condition and +restores one-time logging semantics. Range validation prevents +inconsistent cache state. No remaining path appears to re-enable the +bug, but the protection is limited to the specific opcode range; any +future code that writes to the same cache word must replicate the same +check. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26648_ntoskrnl.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26648_ntoskrnl.exe.txt new file mode 100644 index 0000000..c46a9d5 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26648_ntoskrnl.exe.txt @@ -0,0 +1,114 @@ +{'confidence': 0.13, 'kb': 'KB5055523', 'file': 'ntoskrnl.exe', 'change_count': 111, 'patch_store_uid': '998f4703-7363-48af-b1e4-3bf03bc6fbf9', 'date': 1751828893.8082724, 'cve': 'CVE-2025-26648'} +-------------------------------------------------------------------- +CVE-2025-26648 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel (ntoskrnl.exe) – WOW64 thread-context helpers +PspWow64GetContextThread / PspWow64SetContextThread + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free / race condition (CWE-416) leading to improperly +locked sensitive data (CWE-591) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NtGetContextThread / NtSetContextThread eventually call the internal +helpers PspWow64GetContextThread() or PspWow64SetContextThread() when +the target thread belongs to a 32-bit (x86) WOW64 process. + +Prior to the patch these helpers processed a caller-supplied +PETHREAD ("Thread" argument) without first stopping the target +thread when it was not the current thread. The code: + 1. validated the requested CONTEXT flags, + 2. called PspGetContextThreadInternal() or + PspSetContextThreadInternal(), + 3. copied the register image to / from user memory with + RtlCopyContext(), + 4. touched per-thread CPU-area buffers in the process object. + +Because the thread kept running in parallel the following windows +existed: + • The thread could exit, causing the KTHREAD / ETHREAD object to + be freed and re-used while the helper still held its pointer. + • Concurrent state changes could make the collected context + inconsistent, leaking kernel data or writing stale values back + into a re-allocated object. + +Any subsequent access (e.g. RtlCopyContext, CPU-area I/O) therefore +operated on freed or re-purposed memory – a classic use-after-free +that allowed a local attacker with THREAD_GET/SET_CONTEXT rights to +corrupt kernel memory and elevate privileges. + +Affected parameters / structures + Thread – caller-supplied ETHREAD pointer + v57/ v59 – on-stack EXTENDED_CONTEXT buffers that are filled from + freed memory regions + ETHREAD.KernelApcDisable field – temporarily decremented without + holding a reference, enabling premature rundown + Per-process CPU-area (Process+0x418) – could be written with + attacker-controlled data while unlocked + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – no suspension of remote thread +result = PspGetContextThreadInternal(Thread, ctx, 0, TRUE, 1); +... RtlCopyContext(localBuf, Flags, &wow64Ctx); // may use freed ptr + +// AFTER – thread is frozen first +if (Thread != KeGetCurrentThread()) { + --KeGetCurrentThread()->KernelApcDisable; + status = PsSuspendThread(Thread, NULL); // guarantees liveness + suspended = TRUE; +} +... +if (suspended) { + PsMultiResumeThread(Thread, NULL, 1); + KeLeaveCriticalRegion(); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode → NtSetContextThread / NtGetContextThread → + PspWow64SetContextThread / PspWow64GetContextThread + └─ uses Thread pointer without suspend + ├─ thread terminates / reuses memory + └─ helper continues, touching freed memory ⇒ kernel UAF + +Attack Vector +-------------------------------------------------------------------- +A local, low-privileged process opens a handle to a WOW64 thread it +owns (or can get a handle to) with THREAD_GET_CONTEXT / +THREAD_SET_CONTEXT rights. While repeatedly calling +NtGet/SetContextThread it concurrently terminates or rapidly changes +the target thread, exploiting the race to trigger use-after-free and +achieve arbitrary kernel memory read/write, thus elevating to SYSTEM. + +Patch Description +-------------------------------------------------------------------- +1. Detects remote-thread case: `if (Thread != KeGetCurrentThread())`. +2. Enters a critical region and calls PsSuspendThread() to hard-stop + the target thread, recording a flag. +3. All early-exit paths now unwind by resuming the thread with + PsMultiResumeThread() and leaving the critical region. +4. Type clean-ups and additional parameter validation were added but + are incidental to the fix. + +Security Impact +-------------------------------------------------------------------- +Without the suspension the helpers could dereference an already- +freed ETHREAD/KTHREAD object, allowing privileged memory corruption +and leakage. A successful exploit yields kernel-mode execution and +a full local privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +Suspending the target thread guarantees its object remains allocated +and its context stable for the entire operation, closing the race +window and eliminating the UAF condition. The symmetric resume in +all exit paths prevents new hangs or leaks. No further writable +paths to freed memory remain, making the fix comprehensive. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26649_schannel.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26649_schannel.dll.txt new file mode 100644 index 0000000..78a5702 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26649_schannel.dll.txt @@ -0,0 +1,147 @@ +{'file': 'schannel.dll', 'kb': 'KB5055523', 'change_count': 34, 'patch_store_uid': '7dcbd1d8-ea00-4048-b511-5ff09787d90f', 'date': 1751820803.7289605, 'confidence': 0.32, 'cve': 'CVE-2025-26649'} +-------------------------------------------------------------------- +CVE-2025-26649 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – Secure Channel (schannel.dll) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition +Secondary: CWE-416 Use-after-free (resulting from the race) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Every SSL/TLS security context inside schannel is represented by a +CSslParentContext object. Several SPI entry points (SpApplyControlToken, +SpLsaQueryContextAttributes, SpSetContextAttributes, SslFreeCustomBuffer +and assorted TLS-13 helper paths) access mutable members of this object, +most notably + + +0x08 -> pointer to implementation (‘child’) context + +0x10 -> pointer to state / extension buffers + +0x18 -> pointer to caller-supplied custom scratch buffer + +0x20 -> DWORD size of that custom buffer + +0x08 -> int32 “in-use” counter (legacy refcount) + +Prior to the patch none of these functions performed any real mutual +exclusion – they only used a fragile _InterlockedIncrement / Decrement on +a field at offset +0x08 to detect re-entrance. Because the counter was +updated *after* the pointer dereferences, two threads could still enter +critical sections concurrently and operate on the same CSslParentContext. + +A typical failing sequence: + + T1: SpApplyControlToken() … reads ctx->+18 (custom buffer) + T2: SslFreeCustomBuffer() frees ctx->+18 and zeroes the field + T1: continues to use the stale pointer ⇒ use-after-free / UAF + +Because these calls execute in LSASS/SYSTEM context while the attacker +controls the data inside the freed buffer, the condition can be turned +into local privilege escalation. + +The vulnerable window is any pair (or more) of SPI calls that touch the +same CSslParentContext from different threads belonging to the same logon +session. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – SslFreeCustomBuffer (simplified) +if (a2 != g_dwPackageId || !a1) + return 0; +void *p = *(void **)(a1 + 0x18); // no locking +if (p) { + vtable_free(p, a3); // free + if (a4) + *(void **)(a1 + 0x18) = 0; // zero pointer +} + +// after – Accept/Release helpers +BYTE CSslParentContext::AcceptCall() +{ + return RtlTryAcquireSRWLockExclusive(this); // fast exclusive lock +} +void CSslParentContext::ReleaseCall() +{ + RtlReleaseSRWLockExclusive(this); +} + +// after – SslFreeCustomBuffer usage +if (FeatureEnabled && !AcceptCall(this)) + return SEC_E_TOO_MANY_CONTEXTS; //120 +... +*(vtable)(ctx->+18)->Free(...); +ReleaseCall(this); +``` +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains a valid schannel security context (AcquireCredentials + + InitializeSecurityContext etc.). +2. Thread-A calls SpApplyControlToken / SpSetContextAttributes etc. +3. While Thread-A is inside the function, Thread-B calls + SslFreeCustomBuffer on the same context. +4. Thread-B frees ctx->CustomScratch and zeroes the pointer. +5. Thread-A continues executing with the dangling pointer ⇒ memory + corruption in LSASS. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. Any user-mode process can call the SSPI / SChannel +APIs against its own negotiated context and run the two code paths in +parallel to corrupt LSASS memory, then craft the corruption to achieve +code execution and privilege escalation to SYSTEM. + + +Patch Description +-------------------------------------------------------------------- +The update introduces a proper critical-section primitive around every +entry point that mutates or reads sensitive CSslParentContext state. + +1. New methods + CSslParentContext::AcceptCall() + CSslParentContext::ReleaseCall() + implement an exclusive Slim-Reader/Writer lock (SRWLOCK). Where the + feature switch is disabled, they fall back to the legacy refcount but + now *gate* entry – only the first caller proceeds. + +2. Each previously unsafe function now: + • calls AcceptCall() at the very top; returns SEC_E_TOO_MANY_CONTEXTS + (120) if the lock is already held. + • performs its normal work. + • calls ReleaseCall() in all exit paths. + +3. Pointer operations were updated to use the ‘this’ offsets directly + (strong typing) but no functional changes other than the lock. + +4. SetGenericExtensionBuffers was replaced entirely and no longer touches + shared state without protection. + + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could trigger a use-after-free on process +memory owned by LSASS, reliably leading to elevation of privilege +(SYSTEM) or denial-of-service. The issue is tracked as +CVE-2025-26649 and is rated “Elevation of Privilege”. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added SRW lock provides strict mutual exclusion; only one thread may +operate on a CSslParentContext at a time. All modified functions now +honour the lock on *all* code paths, including early returns and error +handlers, thereby eliminating the race window that allowed concurrent +free/use of the custom scratch buffer and other shared members. No +residual caller-controlled paths bypass the Accept/Release pair, so the +fix is considered effective for the vulnerable entry points observed in +the diff. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26651_lsm.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26651_lsm.dll.txt new file mode 100644 index 0000000..2015639 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26651_lsm.dll.txt @@ -0,0 +1,105 @@ +{'patch_store_uid': '8cc780fd-7b64-45ab-bdc0-860e19f5838a', 'confidence': 0.24, 'file': 'lsm.dll', 'date': 1751828845.3491733, 'change_count': 15, 'cve': 'CVE-2025-26651', 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-26651 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Session Manager (lsm.dll) – RPC server side handlers +for session-management operations. + +Vulnerability Class +-------------------------------------------------------------------- +Logic error / Exposed dangerous function (CWE-749) leading to service +crash (Denial-of-Service). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The RPC method RpcGetSessionIds is exported by the LSM RPC interface. +In the vulnerable build the server stub executes the following code +right after entry: + + DebugBreak(); + return 0x8000FFFF; // E_UNEXPECTED + +DebugBreak() raises STATUS_BREAKPOINT in the calling thread. +Because the call is executed inside the service process (lsm.exe), and +because no structured exception handling surrounds the call, the +exception is unhandled and terminates the Local Session Manager +service. Any caller that can reach the RPC endpoint can therefore +bring down LSM at will, causing a system-wide service disruption (no +new logon sessions, reconnects, etc.). No parameter validation or +privilege check precedes the DebugBreak, so the crash is guaranteed on +every invocation. + +Other large refactoring changes in +CTSSession::ConnectToTerminal/RpcLoggedOnCompleted were made, but they +do not participate in the DoS trigger path; the root cause is the +unconditional DebugBreak in RpcGetSessionIds. + +Structures / parameters involved: + • RPC opnum : unknown (internal ID, not present in diff) + • Server routine : RpcGetSessionIds() + • Raised status : STATUS_BREAKPOINT (0x80000003) via DebugBreak() + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +__int64 RpcGetSessionIds() +{ + DebugBreak(); // unconditionally crash + return 2147500033i64; // E_UNEXPECTED +} + +// after +__int64 RpcGetSessionIds() +{ + if(!wil::details::FeatureImpl<...>::__private_IsEnabled(&impl)) + DebugBreak(); // executed only when feature flag says + // "debug"; disabled in production + return 2147500033i64; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client (attacker) -> LSM RPC endpoint -> opnum for RpcGetSessionIds -> +server routine executes DebugBreak -> STATUS_BREAKPOINT not handled -> +lsm.exe process terminates -> logon/session management unavailable -> +Denial-of-Service. + +Attack Vector +-------------------------------------------------------------------- +Any authenticated entity that can send RPC traffic to the LSM endpoint +(named pipe/ALPC – exact transport unknown) can invoke the vulnerable +method. No special privileges or parameters are required. Remote +reachability is "network" according to the CVE description; deeper +ACL details are unknown. + +Patch Description +-------------------------------------------------------------------- +The fix wraps the DebugBreak call inside a WIL runtime feature check: + + if (!Feature_2578215227::IsEnabled()) + DebugBreak(); + +The corresponding feature flag is disabled in production builds, so +DebugBreak is never executed for normal users. In effect, the crash +vector is removed while still allowing developers to enable it in +instrumented/debug configurations. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could repeatedly crash the LSM service, +preventing user logons and reconnects and leading to a local or remote +Denial-of-Service. No memory-corruption or privilege-elevation was +observed, only service termination. + +Fix Effectiveness +-------------------------------------------------------------------- +Because the dangerous statement is now guarded by a feature flag that +is disabled by default, ordinary deployments can no longer reach the +DebugBreak path. Unless the flag is manually re-enabled (e.g. in an +internal debug build) the attack surface is closed; thus the patch is +considered effective. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26663_wldap32.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26663_wldap32.dll.txt new file mode 100644 index 0000000..1fa5e29 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26663_wldap32.dll.txt @@ -0,0 +1,108 @@ +{'change_count': 13, 'cve': 'CVE-2025-26663', 'confidence': 0.26, 'patch_store_uid': 'ca230859-d37d-4894-8880-e7425bdefdb2', 'file': 'wldap32.dll', 'date': 1751828826.5121112, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-26663 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft wldap32.dll – CryptStream TLS/SSL helper that wraps SChannel +APIs for LDAP over SSL (LDAPS). All affected routines live inside the +client-side CryptStream class (DecryptLdapReceive, LdapSendSsl, +NegotiateSecureConnection, SSPI negotiate loop, TearDownSecureConnection +and the class dtor). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free caused by missing synchronisation around the +SChannel security context that lives inside every CryptStream object. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each CryptStream instance owns an SChannel context handle pair stored at +offsets +64/+72 (now +72/+80 after refactor). The handle is destroyed in +TearDownSecureConnection(), CryptStream::~CryptStream() or during +renegotiation, but the same pointer is dereferenced by worker paths that +process network data – e.g. DecryptLdapReceive() and LdapSendSsl(). + +Before the patch all these paths accessed *(this+64) without any locking +or reference counting: + • DecryptLdapReceive → this[1] (DecryptMessage) + • LdapSendSsl → this[1] (EncryptMessage) + • Negotiate / Tear-down → DeleteSecurityContext +Because LDAP traffic can be serviced by multiple worker threads (async +I/O completion or parallel calls issued by the upper LDAP runtime), a +thread that is still inside DecryptMessage can race with another thread +tearing the context down. Once DeleteSecurityContext frees the kernel +objects the first thread continues to use freed memory, yielding a UAF +that an attacker controlling the network stream can shape. + +Key data affected + offset +48 : CredHandle (creds) + offset +64 : CtxtHandle Lower + offset +72 : CtxtHandle Upper + offset +35* : negotiated stream buffers + SRW lock : newly inserted at (this+6) + +Exploitability stems from the fact that the freed CtxtHandle structure is +heap allocated inside schannel, so an attacker can provoke a predictable +layout and gain EIP/RIP control when the later DecryptMessage() walks the +already-freed structure. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch DecryptLdapReceive excerpt +v18 = this[1]( (char*)this+64, (char*)this+192, 0, 0 ); +// no locking, may run concurrently with DeleteSecurityContext + +// pre-patch destructor +if (*((_OWORD*)this+4)!=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + (*(void(**)(void))(*((_QWORD*)this+3)+72))(); // frees context +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread-A processes network data → DecryptLdapReceive() → uses context. +2. Thread-B handles LDAP disconnect / renegotiation → + TearDownSecureConnection() → DeleteSecurityContext(). +3. Context memory freed while Thread-A still inside schannel → UAF → + memory corruption / RCE. + +Attack Vector +-------------------------------------------------------------------- +A remote LDAP server (or a MiTM attacker) sends crafted packets that +force frequent renegotiation or connection tear-down while the client is +still decoding previous records. With asynchronous receives the two +code paths can execute in parallel, triggering the race and ultimately +use-after-free inside the client process (e.g. LSASS or any process that +uses wldap32). + +Patch Description +-------------------------------------------------------------------- +1. A per-instance SRWLOCK is added and initialised in the constructor. +2. New helpers: + • CryptStream::AcquireSslContextLockExclusive + • CryptStream::ReleaseSslContextLockExclusive + • DecryptLdapReceive/LdapSendSsl/SSPINegotiateLoop acquire the lock in + shared mode while _using_ the context. + • All functions that mutate or free the context (TearDown, dtor, + NegotiateSecureConnection, etc.) acquire the lock in exclusive mode. +3. Old WIL helper code is removed; the Feature flag gates the lock calls + so the change is flight-controlled. + +Security Impact +-------------------------------------------------------------------- +Without the lock a race allows use-after-free of an SChannel context +object, leading to heap memory corruption in a high-privilege process +that speaks LDAP over SSL. An unauthenticated network attacker can +craft traffic to obtain remote code execution in that process context +(typically SYSTEM). + +Fix Effectiveness +-------------------------------------------------------------------- +The SRW lock serialises all operations that access the CtxtHandle: +exclusive writers (delete/renew) cannot run while readers are in +Decrypt/Encrypt, and vice-versa. The handle cannot be freed until the +last shared holder exits, removing the UAF window. No remaining code +paths dereference the context without holding either the shared or +exclusive lock, so the vulnerability is fully mitigated. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26665_upnphost.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26665_upnphost.dll.txt new file mode 100644 index 0000000..34768cd --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26665_upnphost.dll.txt @@ -0,0 +1,128 @@ +{'kb': 'KB5055523', 'file': 'upnphost.dll', 'patch_store_uid': 'db294493-e719-4193-9502-b097e10eee9b', 'confidence': 0.34, 'cve': 'CVE-2025-26665', 'date': 1751822653.5252056, 'change_count': 16} +-------------------------------------------------------------------- +CVE-2025-26665 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows UPnP Device Host service (upnphost.dll). Affected routines +include CRegistrarSingleton::SetICSInterfaces, +CUPnPInterfaceList::HrSetICSOff / HrShutdown, +BaseHttpListener::DoReceiveRequestHeaders, and the helper routines +StringVPrintfWorkerW_*. + +Vulnerability Class +-------------------------------------------------------------------- +Race condition / use-after-free caused by missing synchronisation of +handle-bearing fields. In practice this translates into CWE-591 +(Sensitive data stored in memory that is not properly locked) and a +local Elevation of Privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. COM entry point + CRegistrarSingleton::SetICSInterfaces() is callable by any local + client through the IUPnPRegistrar interface. Before the patch the + function manipulated the **global** thread handle `hObject` and the + per-instance handle stored at offset +26 of + CUPnPInterfaceList::s_instance **without acquiring any lock**. + +2. Handle life-cycle before the patch + a. If `hObject` was non-NULL the code performed + WaitForSingleObject(hObject,INFINITE) followed by CloseHandle. + b. Immediately afterwards CreateThread is invoked and the returned + handle is written back to `hObject` (+26 in the object). + c. Nothing prevents a second caller from executing the same code + path concurrently. Two threads therefore can interleave so + that one thread frees a handle that the other still expects to + be valid (classic use-after-free). + +3. Memory/handle reuse window + Because the service runs as LocalService/SYSTEM, an attacker can + deliberately create kernel objects until the just-freed handle + value is re-allocated for an attacker-controlled object. When the + service later calls WaitForSingleObject or CloseHandle it will + operate on the attacker’s object, enabling privilege-escalation + primitives (e.g. signalling an event in a higher privilege + namespace or closing a handle that should stay open). + +4. Related secondary issues + * CUPnPInterfaceList::HrShutdown() used the same unguarded fields + during service stop, producing an identical race. + * BaseHttpListener::DoReceiveRequestHeaders() relied on an + uninitialised NumberOfBytesTransferred when ERROR_MORE_DATA was + returned – potentially copying stale memory into a heap buffer. + * The two internal StringVPrintfWorkerW_* helpers returned wrong + length information when the buffer was exactly full, causing the + caller to treat uninitialised memory as valid. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – no locking, shared field +26 +DWORD ThreadId = 0; +void *v2 = *((void **)this + 26); +if (v2) { + WaitForSingleObject(v2, INFINITE); + CloseHandle(v2); + *((void **)this + 26) = NULL; +} +*((void **)this + 26) = CreateThread(NULL,0,ExecIcsChangeInterfaces, + this,0,&ThreadId); +``` +```c +// after patch – serialised with critical section and new slot +31 +EnterCriticalSection((LPCRITICAL_SECTION)this + 2); +... +*((void **)this + 31) = CreateThread(...); +LeaveCriticalSection((LPCRITICAL_SECTION)this + 2); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged client obtains IUPnPRegistrar via COM. +2. Client spams SetICSInterfaces() from multiple threads. +3. Races in HrSetICSOff/HrShutdown close and recreate the same handle. +4. Attacker reallocates the freed handle for a controlled object. +5. Service operates on attacker’s handle with LocalService/SYSTEM + privilege, enabling EoP. + +Attack Vector +-------------------------------------------------------------------- +Local. Any authenticated user capable of activating the UPnP Registrar +COM object can trigger the race condition; no additional service +configuration is required. + +Patch Description +-------------------------------------------------------------------- +* Introduces a dedicated critical section (offset +2 in the object) and + wraps all handle manipulation (CloseHandle / CreateThread / Wait*) in + Enter/LeaveCriticalSection pairs. +* Moves the thread-handle field from index +26 to +31, preventing legacy + code paths from touching the new handle unintentionally. +* CUPnPInterfaceList::HrShutdown now serialises shutdown-time handle + cleanup in the same way. +* DoReceiveRequestHeaders now optionally supplies + NumberOfBytesTransferred to HttpReceiveHttpRequest when the new + feature flag is active, preventing use of uninitialised stack memory. +* StringVPrintfWorkerW_* were swapped/fixed so that pcchNewDestLength is + updated correctly and the overflow path no longer leaks stack data. + +Security Impact +-------------------------------------------------------------------- +Without the lock an attacker could: +1. Cause the service to act on a hijacked handle (signal, duplicate or + close), achieving Local Privilege Escalation. +2. Read or corrupt sensitive in-process state because stale pointers + remained accessible. +3. Indirectly disclose memory through the uninitialised-length bug in + DoReceiveRequestHeaders / StringVPrintfWorkerW. + +Fix Effectiveness +-------------------------------------------------------------------- +Serialising all handle operations removes the race window, making +hijacking impossible. Additional hardening (correct buffer length +handling) prevents auxiliary memory-safety problems. The protection is +conditionally compiled behind FeatureId 2578215227; effectiveness +therefore depends on that feature being enabled on the target build, but +it is enabled by default in the shipping patch level. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26668_mprapi.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26668_mprapi.dll.txt new file mode 100644 index 0000000..7ed1df2 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26668_mprapi.dll.txt @@ -0,0 +1,113 @@ +{'date': 1751822603.8228416, 'cve': 'CVE-2025-26668', 'patch_store_uid': 'df5c96ae-bf56-4fc6-929b-b0a727fc0bc8', 'confidence': 0.26, 'file': 'mprapi.dll', 'change_count': 4, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-26668 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) user-mode helper +library mprapi.dll. The same code is linked into the RRAS service +process (svchost.exe –netsvcs), so the flaw is reachable in both the +client API and the server-side RPC handlers. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by integer-truncation/size-validation +errors (CWE-122 + CWE-190). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several exported helper routines allocate an output buffer whose length +is computed as <record_size> * <count_returned_by_RPC>. Before the +patch the product was cast to 32-bit and supplied to LocalAlloc as an +unsigned int, while all subsequent memory operations (memset, memcpy, +or structure field access) used the full 64-bit value. If the 64-bit +product exceeds 0xFFFFFFFF the cast silently truncates, causing an +allocation that is much smaller than the space later written. The +result is an unconstrained heap overwrite in the RRAS process running +as NT AUTHORITY\SYSTEM. + +Affected helper functions and record sizes: + • MprAdminInterfaceEnumEx – record 1280 bytes (0x500) + • MprAdminRoutingDomainsEnumEx – record 540 bytes (0x21C) + • MprAdminConnectionEnumEx – record 1672 bytes (0x688) + +The function MprAdminInterfaceDeviceGetInfo contains a second, related +issue: the routine dereferences a pointer at offset 0x228 (552) inside +the structure returned by the RPC call without first validating that +the buffer is large enough. A short buffer therefore allows the +attacker to redirect subsequent pointer writes anywhere on the heap. + +In all cases the attacker controls the record count and/or buffer size +via crafted RPC replies to procedure numbers 0x26, 0x37, 0x3E and 0x2D +in the \PIPE\ROUTER endpoint. No authentication is required when RRAS +is configured for remote access. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// MprAdminInterfaceEnumEx – BEFORE +v16 = (char *)LocalAlloc(0x40u, (unsigned int)(1280 * *v11)); +memset_0(v16, 0, 1280i64 * (unsigned int)*v11); +for ( i = 0; i < *v11; ++i ) + Pointer = ConvertMpriToMprStructureInternal(...); +``` + +```c +// MprAdminRoutingDomainsEnumEx – BEFORE +v6 = (char *)LocalAlloc(0x40u, (unsigned int)(540 * *a3)); +while (v9 < *a3) + Pointer = ConvertMpriToMprStructureInternal(...); +``` + +```c +// MprAdminInterfaceDeviceGetInfo – BEFORE +if (dwLevel == 1 && *((QWORD*)&v7+1)) + *(QWORD*)(*((QWORD*)&v7+1) + 552) = *((QWORD*)&v7+1) + 560; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote attacker sends crafted RPC request to RRAS procedure 0x37 + (e.g. RouterInterfaceEnumEx). +2. RRAS server unmarshals and calls MprAdminInterfaceEnumEx. +3. RPC stub fills *Entries with a large value (>= 0x10000000) and + returns variable-length data blob in *hMem. +4. Function multiplies 0x500 * Entries, truncates to 32-bit, allocates + a tiny buffer, then copies the full data, overflowing the heap. +5. Overwritten heap metadata or function pointers are later used, + leading to code execution under SYSTEM. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker communicates with the vulnerable RRAS +named-pipe/RPC interface and returns a maliciously large entry count or +short structure in the RPC response. No credentials are required when +RRAS is exposed to the network. + +Patch Description +-------------------------------------------------------------------- +1. All allocation sizes are now tracked in 64-bit variables. Before + calling LocalAlloc the code checks + if (product > 0xFFFFFFFF) { return ERROR_INVALID_PARAMETER; } + thereby preventing truncation. +2. MprAdminInterfaceDeviceGetInfo verifies that the returned level-1 + structure is at least 0x230 bytes. Short buffers are freed and + ERROR_INVALID_PARAMETER (87) is returned. +3. New validation is gated by Feature_986203450__private_... + but the default state enables the checks. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a remote attacker could overflow the process heap in +the RRAS service and achieve arbitrary code execution with SYSTEM +privileges, leading to complete server compromise. + +Fix Effectiveness +-------------------------------------------------------------------- +Code inspection shows that all previously unguarded multiplications are +now size-checked and the minimum structure length is enforced before +any pointer dereference. Provided the feature flag is not disabled via +configuration, the original attack paths are blocked. No alternative +unchecked allocation paths were observed in the diff, but full binary +review is required to rule out other occurrences. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26669_mprapi.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26669_mprapi.dll.txt new file mode 100644 index 0000000..4c2a6d5 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26669_mprapi.dll.txt @@ -0,0 +1,130 @@ +{'patch_store_uid': 'df5c96ae-bf56-4fc6-929b-b0a727fc0bc8', 'kb': 'KB5055523', 'change_count': 4, 'confidence': 0.28, 'cve': 'CVE-2025-26669', 'file': 'mprapi.dll', 'date': 1751829055.6001742} +-------------------------------------------------------------------- +CVE-2025-26669 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) user-mode RPC client +library, mprapi.dll. Affected exported APIs: + • MprAdminInterfaceDeviceGetInfo + • MprAdminInterfaceEnumEx + • MprAdminRoutingDomainsEnumEx + • MprAdminConnectionEnumEx +All four wrappers are executed inside the calling process and parse the +network data returned by the RRAS server. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-125: Out-of-bounds Read (triggered by integer-truncation that leads +to an undersized heap allocation and subsequent out-of-bounds memory +access; some paths can also over-write, but Microsoft classifies as +information disclosure). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The four helper functions retrieve variable-length arrays or structures +from the remote RRAS server via the auto-generated RPC stub +NdrClientCall3(). + +Returned parameters that remain fully controlled by the server are: + • entry count (for *EnumEx helpers) -> DWORD *a5 / *a3 / v17 + • structure size (for *DeviceGetInfo) -> DWORD LOWORD(v7) + +PRE-PATCH logic +1. NdrClientCall3() returns success and fills the count / size as + 32-bit values. +2. The wrapper multiplies the count with a fixed element size to obtain + the required buffer length: + bytes = (unsigned int)(STRUCT_SIZE * Count); +3. The product is explicitly cast to unsigned int and supplied to + LocalAlloc(). +4. If Count is large enough ( >= 0x100000000 / STRUCT_SIZE ), the 64-bit + product is truncated to 32 bits. LocalAlloc therefore returns a + buffer that is far smaller than needed. +5. A for-loop copies every element that the server claimed to return + into the undersized buffer using memcpy() / manual field copies: + memcpy(Buffer + i*STRUCT_SIZE , Remote + i*STRUCT_SIZE , ...); +6. When i exceeds bytes/STRUCT_SIZE the code walks past the allocation + boundary and reads (and partly writes) adjacent process memory, + leaking it back to the caller or potentially corrupting heap + metadata. + +The same pattern exists in three enumeration helpers with element sizes + • 1280 (MprAdminInterfaceEnumEx) + • 540 (MprAdminRoutingDomainsEnumEx) + • 1672 (MprAdminConnectionEnumEx) + +For MprAdminInterfaceDeviceGetInfo the defect is slightly different: the +code blindly dereferences offset 0x230 and 0x238 inside the returned +LEVEL-1 structure without verifying that the server allocated that many +bytes. A size field smaller than 0x230 results in an out-of-bounds read +from heap memory. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// InterfaceEnumEx – before patch +Pointer = (unsigned int)NdrClientCall3(...).Pointer; +if (!Pointer) { + char *buf = LocalAlloc(0x40u, (unsigned int)(1280 * *a5)); + ... + for (i=0; i < *a5; i++) + memcpy(buf + 1280*i, (char*)hMem + 1280*i, 1280); +} + +// DeviceGetInfo – before patch +if (dwLevel == 1) { + if (*((QWORD*)&v7 + 1) && *(QWORD*)(*((QWORD*)&v7 + 1)+552)) + *(QWORD*)(*((QWORD*)&v7 + 1)+552) = *((QWORD*)&v7 + 1)+560; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker controls a malicious RRAS server. +2. Victim calls any affected *EnumEx / *GetInfo API to that server. +3. Server crafts RPC reply with: + Count = 0xFFFFFFFF (or any value causing overflow) + OR size < 0x230 for GetInfo(Level1) +4. Client-side mprapi.dll allocates a truncated heap buffer. +5. Copy loop (or pointer fix-up) accesses beyond the buffer. +6. Out-of-bounds data is disclosed to the caller or can corrupt heap + memory leading to further exploitation. + +Attack Vector +-------------------------------------------------------------------- +Network. A non-privileged attacker who can impersonate or operate a +RRAS server that the victim queries can send specially crafted RPC +responses to trigger the flaw in the client process. + +Patch Description +-------------------------------------------------------------------- +Microsoft added strict size validation guarded by the internal runtime +flag Feature_986203450__private_IsEnabledDeviceUsageNoInline(): + • For each EnumEx helper the 64-bit product is computed first and + compared against 0xFFFFFFFF. If it would overflow, the function now + aborts with ERROR_INVALID_PARAMETER (87) before allocation. + • DeviceGetInfo now validates that the returned structure length is + >= 0x230 bytes. If not, the buffer is freed and the same error 87 + is returned. + • All functions free any partially allocated memory on early exit to + avoid leaks. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a malicious RRAS server could force the caller to +read past the end of a heap allocation, disclosing up to several +kilobytes of process memory per request (information disclosure). In +some cases the overwrite could corrupt adjacent heap data and be +leveraged for code execution, but only information disclosure is +acknowledged in CVE-2025-26669. + +Fix Effectiveness +-------------------------------------------------------------------- +The added 64-bit multiplication and upper-bound check eliminates the +integer truncation, making it impossible to obtain an undersized buffer +through a crafted count. The Level-1 structure length check prevents +pointer fix-up on an undersized buffer. No residual code paths +reachable from the same APIs perform unchecked size calculations, so +the patch fully mitigates the identified out-of-bounds read. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26669_rasman.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26669_rasman.dll.txt new file mode 100644 index 0000000..5de3b7d --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26669_rasman.dll.txt @@ -0,0 +1,125 @@ +{'date': 1751829059.949632, 'kb': 'KB5055523', 'change_count': 1, 'cve': 'CVE-2025-26669', 'patch_store_uid': '9cd94eb6-b985-4fb4-855d-4c1a18baab7b', 'confidence': 0.72, 'file': 'rasman.dll'} +-------------------------------------------------------------------- +CVE-2025-26669 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) user-mode helper +module rasman.dll – function SubmitRequest() that marshals caller +parameters into a service request and later demarshals the service +response back into the caller’s buffers. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read / Information Disclosure (CWE-125) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SubmitRequest() handles many request op-codes. For op-code 0x5E +(DeviceUsage) it copies variable-length data returned by the remote +RRAS service from an internal heap buffer (v11) into a caller supplied +output buffer (v4). + +Prior to the fix the size check that protects this memcpy was executed +only when the undocumented feature flag +Feature_2659568953__private_IsEnabledDeviceUsageNoInline() evaluated to +TRUE. On systems where the feature is disabled that guard is skipped +entirely: + + if (!featureEnabled) { + /* no validation */ + memcpy(v4, v11 + 40, *((DWORD*)v11 + 8)); + } + +The length in *((DWORD*)v11 + 8) is fully under the control of the +remote RRAS service and can be made larger than the actual allocation +that starts at (v11 + 40). When this happens memcpy() reads beyond +the end of the heap allocation, copying stale heap data into the +caller’s buffer which is ultimately forwarded to the (unauthenticated) +network peer. No crash occurs because the destination buffer size is +checked elsewhere; only the source is unchecked, producing an +information leak. + +Affected variables / structures + v11 – pointer to the service response buffer obtained from + AllocateBuffer(). + *((DWORD*)v11 + 8) – 32-bit length field returned by the service and + copied verbatim into the request packet. + Size – size of the caller supplied output buffer, passed on the + stack. + featureEnabled – result of the above feature flag helper; FALSE on + all released Windows builds where the vulnerability is + observable. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* before patch – size check hidden behind feature flag */ +if ((unsigned)Feature_IsEnabledDeviceUsageNoInline(v6,&_ImageBase)) { + if (*((DWORD*)v11 + 8) > (unsigned)Size) + v13 = 603; // ERROR_BUFFER_TOO_SMALL + else + memcpy_0(v4, v11 + 40, *((DWORD*)v11 + 8)); +} else { + /* !!! NO VALIDATION – OOB READ !!! */ + memcpy_0(v4, v11 + 40, *((DWORD*)v11 + 8)); +} + +/* after patch – guard executed unconditionally */ +if (*((DWORD*)v11 + 8) > (unsigned)Size) { + v13 = 603; // refuse to copy +} else { + memcpy_0(v4, v11 + 40, *((DWORD*)v11 + 8)); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker establishes an RRAS connection and causes the client to + call RasSubmitRequest(opCode 0x5E). +2. Attacker’s RRAS server crafts a reply whose length field + (Offset 0x20 in the response packet => *((DWORD*)v11 + 8)) is larger + than the real payload. +3. Client receives the reply; SubmitRequest() allocates a 0x1C00-byte + buffer, copies the packet, and – because the feature flag is FALSE – + directly memcpy()s the claimed length into the caller buffer. +4. Bytes past the end of the allocation are read and returned to the + caller and can be sent back to the attacker, leaking heap memory + from the RRAS process. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker operating a malicious RRAS server or +MITM position induces a Windows client to connect (e.g., via VPN +profile). No local privileges are required. + +Patch Description +-------------------------------------------------------------------- +The update makes the bounds check unconditional and consistent: + • The entire feature-flag branch was removed; validation now always + executes. + • *(DWORD*)(v11 + 32) (length) is set from the trusted Size variable + instead of an alias that could refer to unrelated stack data. + • Identical check is added when exporting the response back to the + caller. + • Numerous cosmetic refactorings (variable renames, logging GUID + updates) but no behavioural change beyond the added validation. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could obtain up to 0x1C00-40 bytes of heap +memory per request, potentially exposing sensitive information such as +VPN credentials or heap addresses useful for further exploitation. +The vulnerability enables passive information disclosure only; no +remote code execution is believed possible through this specific read. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code validates the length every time regardless of feature +flags, returning ERROR_BUFFER_TOO_SMALL if the server-supplied length +exceeds the caller’s buffer. No code paths remain that can copy data +without this comparison, effectively eliminating the out-of-bounds +read. + +-------------------------------------------------------------------- \ No newline at end of file diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26670_wldap32.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26670_wldap32.dll.txt new file mode 100644 index 0000000..afea4d8 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26670_wldap32.dll.txt @@ -0,0 +1,117 @@ +{'kb': 'KB5055523', 'change_count': 13, 'file': 'wldap32.dll', 'patch_store_uid': 'ca230859-d37d-4894-8880-e7425bdefdb2', 'cve': 'CVE-2025-26670', 'date': 1751828833.978264, 'confidence': 0.23} +-------------------------------------------------------------------- +CVE-2025-26670 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – wldap32.dll (LDAP client) CryptStream TLS/SSL +handling code (functions such as LdapSendSsl, DecryptLdapReceive, +NegotiateSecureConnection, TearDownSecureConnection and +CryptStream destructor). + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / concurrent access to freed object (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each CryptStream object owns an SChannel security context structure +(‘SslContext’) stored at offset 0x40 (pre-patch) / 0x48 (post-patch). +The context is released in several places: + • CryptStream::TearDownSecureConnection() + • CryptStream::~CryptStream() + • error paths inside NegotiateSecureConnection() + +Before the patch these functions simply called the SChannel helper +(*(ctx->vtable+72)) and then overwrote the two QWORDs holding the +context with 0xFFFFFFFF… *without* synchronising with other threads. + +At the same time other threads in the same LDAP connection invoked +crypto helpers that dereference the very same context, e.g. in + • LdapSendSsl() – EncryptMessage / QueryContextAttributesW + • DecryptLdapReceive() – DecryptMessage / QueryContextAttributesW + • SSPI negotiate loop – InitializeSecurityContextW, etc. +These readers ran in parallel and performed indirect calls through +the freed context, leading to use-after-free and arbitrary code +execution in the client process. + +Patch analysis shows a new SRWLOCK embedded in CryptStream +(this+6). Two helper wrappers were introduced: + • CryptStream::AcquireSslContextLockExclusive/Release… + • CryptStream::AcquireSslContextLockShared/Release… +All writers of the context (free, re-initialise) now take the lock in +exclusive mode, while all readers take it in shared mode. The +embedded lock is initialised in the constructor. + +The bug therefore was the lack of any lifetime synchronisation for the +shared security context inside CryptStream, allowing concurrent +threads to dereference it after it had been freed by another thread. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – no locking, context freed while in use +CryptStream::~CryptStream() { + if (*((QWORD*)this+6) != 0xFFFFFFFFFFFFFFFF) + (*(void(**)())(*((QWORD*)this+3)+72))(); // free ctx + ... // no synchronisation +} + +// AFTER – context protected by SRWLOCK +CryptStream::~CryptStream() { + CryptStream::AcquireSslContextLockExclusive(this); + if (*((QWORD*)this+9) != 0xFFFFFFFFFFFFFFFF) + (*(void(**)())(*((QWORD*)this+3)+72))(); + CryptStream::ReleaseSslContextLockExclusive(this); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread-A calls ldap_unbind() → CryptStream::TearDownSecureConnection + → frees SslContext (no lock) and returns. +2. Thread-B is still inside LdapSendSsl() or DecryptLdapReceive() + and calls (*ContextVTBL+48) / QueryContextAttributes on the stale + pointer. +3. Indirect call lands in freed / attacker-controlled memory – memory + corruption → RCE. + +Attack Vector +-------------------------------------------------------------------- +A malicious or compromised LDAP server can keep several application +threads busy (e.g. by causing referrals) and then induce a second +thread to close the connection while the first thread is still inside +crypto routines, creating the UAF race. No local privileges are +required; the attack is performed over the network. + +Patch Description +-------------------------------------------------------------------- +• Added an RTL_SRWLOCK field to CryptStream and initialised it in the + constructor. +• New helper wrappers implement Acquire/Release in shared or + exclusive mode. +• All read-only crypto operations (send/receive, negotiate loop) + now call AcquireSslContextLockShared() / Release…Shared(). +• All functions that free or re-initialise the SslContext + (destructor, TearDownSecureConnection, NegotiateSecureConnection + failure paths) take the lock in exclusive mode. +• LdapSendSsl was re-written to use the shared lock around the + EncryptMessage call. +• Accessor added to query SSL attributes in a locked fashion. + +Security Impact +-------------------------------------------------------------------- +Without the lock a remote attacker could win the race and cause the +LDAP client to execute code at the address of a freed SslContext +vtable pointer. The bug therefore enables remote code execution in +any Windows component that uses wldap32.dll (e.g. Active Directory +Tools, Outlook, etc.). Local privilege is not required. + +Fix Effectiveness +-------------------------------------------------------------------- +The added SRWLOCK serialises all readers and writers of the security +context. A writer (free) now waits until all readers complete, and +readers that start afterwards will see either a valid context or the +0xFFFFFFFF sentinel. No other functional changes are visible, so the +patch fully mitigates the identified UAF race. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26672_mprapi.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26672_mprapi.dll.txt new file mode 100644 index 0000000..5e79218 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26672_mprapi.dll.txt @@ -0,0 +1,112 @@ +{'cve': 'CVE-2025-26672', 'change_count': 4, 'kb': 'KB5055523', 'confidence': 0.2, 'file': 'mprapi.dll', 'date': 1751822595.271052, 'patch_store_uid': 'df5c96ae-bf56-4fc6-929b-b0a727fc0bc8'} +-------------------------------------------------------------------- +CVE-2025-26672 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) user-mode helper +library mprapi.dll. Affected entry points are + • MprAdminInterfaceDeviceGetInfo + • MprAdminInterfaceEnumEx + • MprAdminRoutingDomainsEnumEx + • MprAdminConnectionEnumEx +These RPC client stubs are reachable through the RRAS management +interface (MS-RRASAPI). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-126: Buffer Over-read caused by missing length validation and 32/64 +bit size truncation (integer overflow). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable helpers transform data returned by the RRAS RPC server +into local structures that are subsequently delivered to the caller. +The server controls two critical length fields: + • *EntriesRead / *TotalEntries – number of array elements to copy + • The first DWORD inside the level-1 DEVICE_INFO structure – its + documented cbSize. + +1. MprAdminInterfaceDeviceGetInfo (level==1) + --------------------------------------------------------------- + Original code blindly accessed offset 0x228 (552) inside the + returned buffer: + if (*(QWORD*)(buf+552)) *(QWORD*)(buf+552)=buf+560; + When cbSize < 0x230 the read crosses the end of the allocation and + discloses adjacent heap contents to the caller. + +2. *EnumEx helpers (Interface / RoutingDomain / Connection) + --------------------------------------------------------------- + The implementation allocates the destination array with: + LocalAlloc(..., (unsigned int)(ElemSize * Entries)) + On 64-bit builds ElemSize*Entries is first truncated to 32 bits; a + large Entries value supplied by the server therefore allocates far + less memory than required. Subsequent per-element memcpy or + ConvertMpriToMprStructureInternal() loops iterate Entries times and + read past the end of the undersized buffer supplied by RPC, leaking + heap data into the returned array. + + Structure sizes involved + InterfaceEx : 1280 bytes + RoutingDomain : 540 bytes + ConnectionEx : 1672 bytes + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – truncated allocation (InterfaceEnumEx) +char *dst = LocalAlloc(0x40, (unsigned int)(1280 * *Entries)); +... +for(i=0; i<*Entries; ++i) + ConvertMpriToMprStructureInternal(...); +``` +```c +// before patch – unchecked size field (DeviceGetInfo) +if(*(QWORD*)(p+552)) *(QWORD*)(p+552) = p + 560; // OOB read +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Attacker (unauthenticated network) -> RRAS RPC interface -> +Server replies with crafted Entries/Size fields -> mprapi.dll helper +allocates/traverses buffer -> over-reads heap -> helper returns +buffer containing unintended memory to attacker. + +Attack Vector +-------------------------------------------------------------------- +Remote attacker sends a crafted MS-RRASAPI RPC request that forces the +server to return malicious enumeration or device information data. +Because the client stub lives in the RRAS service process itself, the +attacker can trigger it over the network with no local privileges and +receive the leaked memory in the RPC response. + +Patch Description +-------------------------------------------------------------------- +The update inserts centralised parameter validation guarded by +Feature_986203450__private_IsEnabledDeviceUsageNoInline() and falls +back to legacy behaviour when the feature flag is disabled. +Key changes: + • Verify dwLevel and cbSize: if cbSize < 0x230 return ERROR_INVALID_ + PARAMETER (87) and free the server buffer. + • Before allocating, compute RequiredSize = ElemSize * Entries in + 64-bit precision and reject when RequiredSize > 0xFFFFFFFF. + • All early-exit paths now free any temporary allocations. + • Common error code paths consolidated to avoid double-free. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a malicious RRAS peer could coerce the service into +reading beyond the bounds of heap allocations and embed the leaked +bytes in the RPC reply. The memory disclosure may include heap +headers, pointers or previously processed secrets, aiding further +exploitation or information gathering. + +Fix Effectiveness +-------------------------------------------------------------------- +The added 64-bit size checks eliminate the integer truncation, and the +cbSize sanity test prevents out-of-bounds field dereferencing. All +paths that formerly leaked now abort with ERROR_INVALID_PARAMETER or +ERROR_NOT_ENOUGH_MEMORY, and allocated memory is freed. Assuming the +feature flag is enabled system-wide, the patch fully addresses the +identified over-read conditions. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26673_samsrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26673_samsrv.dll.txt new file mode 100644 index 0000000..eb6b4c8 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26673_samsrv.dll.txt @@ -0,0 +1,114 @@ +{'file': 'samsrv.dll', 'kb': 'KB5055523', 'change_count': 1, 'cve': 'CVE-2025-26673', 'patch_store_uid': '34c965b4-c42b-4851-b6ce-cb58c31d6c36', 'date': 1751828833.257583, 'confidence': 0.2} +-------------------------------------------------------------------- +CVE-2025-26673 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows SAM server (samsrv.dll) – function +wil_details_FeatureReporting_RecordUsageInCache() + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation leading to uncontrolled resource +consumption (CWE-400) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RecordUsageInCache maintains a 64-bit bit-set that tracks whether a +particular “feature id” (parameter a3) has already been reported. A +feature id in range 320-383 is mapped to bit positions 0-63 by + + v7 = a3 - 320; // wanted: 0 <= v7 < 64 + +The pre-patch code checked only + + if ((int)(a3 - 320) < 64) + +This expression is true both for the intended positive values and for +any negative result (e.g. a3 == 8 gives -312 < 64). Because v7 is +unsigned, negative values are converted to very large numbers +(0xFFFFFEC8, …). Those bogus indices are then folded to 16 bits and +mixed into the shared bit-field by a CAS loop: + + new = v8 ^ ((uint16)v8 ^ (uint16)(32 * v7)) & 0x7E0 | 0x10; + +With an out-of-range v7 the loop never observes the expected value and +spins indefinitely, saturating one CPU core. Every client request that +passes an attacker-controlled feature id < 320 but still routed through +the “6+” branch can therefore pin a worker thread, exhausting CPU and +thread pool resources in LSASS and resulting in an LDAP service denial. + +Additional problem: even for legal IDs, the routine unconditionally +creates a new usage record, allowing unbounded growth of the in-memory +cache when the same feature is reported repeatedly. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch +v7 = a3 - 320; +if ((int)(a3 - 320) < 64) // negative values also pass! +{ + v8 = *((DWORD*)a2 + 1); + do { + v9 = (v8 & 0x10) && (((v8 >> 5) & 0x3F) == v7); + v6[4] = v9; + v10 = v8; + v8 = _InterlockedCompareExchange(a2+1, + v8 ^ ((uint16)v8 ^ (uint16)(32 * v7)) & 0x7E0 | 0x10, + v8); + } while (v10 != v8); +} +``` +```c +// post-patch +if ((int)(a3 - 320) >= 64) // bail out on invalid index + goto LABEL_16; +... +if (!v6[4]) { // only if not already set + v6[2] = a3; + v6[1] = 1; + v6[3] = a4; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote LDAP call reaches SAM through LSASS. +2. SamrIsShadowAdminAccount() +3. Feature_Adminless_..._IsEnabledDeviceUsageNoInline() +4. wil_details_FeatureReporting_ReportUsageToService() +5. wil_details_FeatureReporting_ReportUsageToServiceDirect() +6. wil_details_FeatureReporting_RecordUsageInCache(a1,a2,a3,a4) + – faulty bounds check executed. + +Attack Vector +-------------------------------------------------------------------- +An unauthenticated attacker sends a crafted LDAP request that causes LSASS +to call RecordUsageInCache with a feature id below 320 but still routed +through the “opportunity/usage” path (a3 >= 8). Each request forces the +CAS retry loop to spin, monopolising CPU and threads and finally causing +LSASS/LDAP to stop responding. + +Patch Description +-------------------------------------------------------------------- +1. Validates the computed bit index: if (a3-320) is not in 0-63 the code + skips the compare-exchange loop (prevents endless spinning). +2. After a successful update the code creates a new cache record only + when the bit was previously clear (prevents cache bloat). + +Security Impact +-------------------------------------------------------------------- +Before the patch, a remote attacker could repeatedly trigger the inner +spin loop, leading to sustained 100% CPU utilisation in LSASS, thread +starvation, and denial of LDAP and other directory-related services. +No privileges are required and no memory corruption occurs, but service +availability is lost. + +Fix Effectiveness +-------------------------------------------------------------------- +The added signed range check guarantees that only indices 0-63 reach the +CAS loop, eliminating the uncontrolled CPU consumption path. The +additional duplication guard stops unbounded cache growth. Assuming no +other entry points bypass the same function, the fix appears complete. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26674_windows.media.playback.mediaplayer.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26674_windows.media.playback.mediaplayer.dll.txt new file mode 100644 index 0000000..fe219d1 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26674_windows.media.playback.mediaplayer.dll.txt @@ -0,0 +1,115 @@ +{'file': 'windows.media.playback.mediaplayer.dll', 'cve': 'CVE-2025-26674', 'patch_store_uid': 'dde755f3-6cfd-4bb7-86ec-3f14fa2eea1c', 'date': 1751829083.0720763, 'confidence': 0.38, 'change_count': 1, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Media Player implementation inside windows.media.playback. +mediaplayer.dll, function MediaPlayerImpl::WaitForVBlankLoop(). The +code synchronises video frame presentation with the monitor V-Blank by +periodically calling IDXGIOutput::WaitForVBlank(). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based Buffer Overflow / NULL-pointer function-pointer dereference +(CWE-122). Absence of a NULL check on a COM interface pointer allows +indirect calls through address 0, which can be steered into a heap +controlled area. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The loop keeps a cached IDXGIOutput* in local variable v6. When the +pointer is NULL the helper GetDefaultDXGIOutput(&v6) is supposed to +populate it. The unpatched logic performs the following sequence: + + 1. v4 = v6; // copy cached pointer + 2. if (!v6) { // attempt to refresh + InternalRelease(&v6); // no-op when v6==NULL + GetDefaultDXGIOutput(&v6); // may fail and leave v6==NULL + v4 = v6; // copy again (may still be NULL) + } + 3. ((IDXGIOutput*)v4)->lpVtbl-> // unconditional indirect call + WaitForVBlank(v4); + +When GetDefaultDXGIOutput fails, v4 remains NULL but the indirect call +is still executed. The first CPU read dereferences address 0 to obtain +the v-table pointer, and the second read fetches the function pointer. +If an attacker is able to allocate heap memory at or near address 0, or +otherwise map controlled data there, the call dispatches to an +attacker-supplied address, achieving arbitrary code execution inside the +Media Player process. Even without exploiting the control-flow hijack, +this causes a write to or read from unmapped memory, leading to a heap +buffer overflow and process crash. + +Key parameters / structures affected + • v6 : local IDXGIOutput* (cached DXGI output) + • v4 : transient copy of v6 used for the virtual call + • GetDefaultDXGIOutput() : helper that may legitimately fail, e.g. + when no physical display is present or the last monitor is + disconnected. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// unpatched excerpt +v4 = v6; +if ( !v6 ) { + InternalRelease(&v6); + GetDefaultDXGIOutput(&v6); + v4 = v6; +} +if ( ((int (__fastcall *)(IDXGIOutput *))v4->lpVtbl->WaitForVBlank)(v4) + < 0 ) { + InternalRelease(&v6); + Sleep(0x10); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker detaches all monitors or otherwise forces + GetDefaultDXGIOutput() to fail. +2. MediaPlayerImpl::WaitForVBlankLoop executes; v6 is NULL. +3. Function still issues v4->lpVtbl->WaitForVBlank(v4) with v4==NULL. +4. Dereference of address 0 results in control-flow through attacker + controlled data located at the NULL page or nearby heap memory. +5. Arbitrary code executes in the context of the calling process, or + the process crashes. + +Attack Vector +-------------------------------------------------------------------- +Local attacker running with the ability to start Media Player and adjust +the display topology. No special privileges are required. Exploitation +is fully local but can be used as a post-exploitation primitive to gain +code execution in another, possibly more privileged, process hosting the +MediaPlayerImpl component. + +Patch Description +-------------------------------------------------------------------- +The fix wraps the pointer acquisition and the virtual call in a single +composite conditional: + + if ( !v6 && (reacquire failed || v4==NULL) || + WaitForVBlank(v4) < 0 ) + +If GetDefaultDXGIOutput fails and v4 is still NULL, the code now skips +the virtual call and falls back to the error path (release + sleep), +thereby eliminating the unsafe dereference. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could induce an indirect call via a NULL +COM interface pointer and thus redirect execution to controlled heap +memory, achieving arbitrary code execution in the context of +windows.media.playback.mediaplayer.dll. At minimum this led to a +reliable denial-of-service. With additional heap manipulation it can be +converted into a full local RCE. + +Fix Effectiveness +-------------------------------------------------------------------- +The added NULL test guarantees that WaitForVBlank is never invoked on a +NULL IDXGIOutput pointer. As long as GetDefaultDXGIOutput cannot return +an object whose lifetime is shorter than the current iteration (not +observed in the diff), the use-after-free and NULL dereference avenues +are closed. No further issues are evident in the patched logic. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26678_manageci.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26678_manageci.dll.txt new file mode 100644 index 0000000..228fc1d --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26678_manageci.dll.txt @@ -0,0 +1,108 @@ +{'file': 'manageci.dll', 'patch_store_uid': '328f9580-dea5-4d7c-927b-5d1aa4fa76f4', 'confidence': 0.16, 'kb': 'KB5055523', 'date': 1751822591.7927551, 'change_count': 5, 'cve': 'CVE-2025-26678'} +-------------------------------------------------------------------- +CVE-2025-26678 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Defender Application Control (WDAC) user-mode runtime, +manageci.dll, function +wil::details::FeatureImpl<__WilFeatureTraits_Feature_CadTest>:: +ReportUsage. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Logic error (CWE-284). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine is responsible for forwarding feature-usage +telemetry from WDAC to the service layer through the helper +wil::details::ReportUsageToService(). + +Before the fix the second parameter of ReportUsage was declared as an +unsigned 8-bit value (a2). The routine copied this byte into v6 and +passed it verbatim as the sixth argument of ReportUsageToService: + + ReportUsageToService(..., &v9, v6, 0); + +No range or consistency checks were performed. Because the function +is exported and can be invoked from any local process linked against +manageci.dll, an unprivileged caller could supply an arbitrary +ReportingKind value. Internally, ReportingKind determines whether WDAC +expects the call to come from *trusted* code (value 1) or from *test / +experimental* flows (value 0 or >1). Supplying a non-standard value +made ReportUsageToService treat the request as coming from a privileged +path and skip normal policy validation, effectively disabling WDAC +checks for the associated feature. + +Additional side effects fixed together with the main flaw: +* The cache value (v4) was handled as 64-bit although only the lower + 32 bits are used. High dword contents were therefore undefined. +* The stage field (v10) was set to 2 instead of the correct 3, causing + a misleading state to be reported. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable version +int v6 = a2; // caller-controlled +... +return ReportUsageToService( + a1 + 2, + 49688645i64, + ((unsigned int)v4 >> 10) & 1, + ((unsigned int)v4 >> 11) & 1, + &v9, + v6, // unvalidated ReportingKind + 0); + +// fixed version +v9 = 3; +... +return ReportUsageToService( + a1 + 2, + 49688645i64, + (v4 >> 10) & 1, + (v4 >> 11) & 1, + &v8, + 1, // constant, validated + 0); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local code loads manageci.dll and calls + FeatureImpl<...>::ReportUsage(..., EvilKind, ...). +2. Function copies EvilKind into v6 without checks. +3. ReportUsageToService is invoked with the forged ReportingKind. +4. Service path assumes privileged caller and records the feature as + enabled without applying WDAC enforcement, allowing a bypass. + +Attack Vector +-------------------------------------------------------------------- +Any local process that can load or import manageci.dll can directly +invoke ReportUsage with a crafted second argument to neutralise WDAC +restrictions for the CadTest feature. + +Patch Description +-------------------------------------------------------------------- +1. The prototype was changed: second parameter widened to 64-bit and is + no longer used in the body. +2. The callee now hard-codes ReportingKind to 1 (trusted path). +3. Stage value initialised to 3, and the cache variable v4 is now + unsigned 32-bit, removing undefined upper bits. + +Security Impact +-------------------------------------------------------------------- +The original implementation allowed unprivileged code to suppress WDAC +policy enforcement for the affected feature, enabling execution of code +that should have been blocked. An attacker could thus bypass a core +Windows security boundary locally. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes all external influence over the ReportingKind field +and corrects auxiliary state handling, eliminating the identified +bypass. No alternative uncontrolled inputs to ReportUsageToService +remain evident in the updated code, so the fix appears complete. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26679_rpcrtremote.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26679_rpcrtremote.dll.txt new file mode 100644 index 0000000..fc64b06 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26679_rpcrtremote.dll.txt @@ -0,0 +1,126 @@ +{'cve': 'CVE-2025-26679', 'confidence': 0.24, 'file': 'rpcrtremote.dll', 'date': 1751829076.7962718, 'kb': 'KB5055523', 'change_count': 1, 'patch_store_uid': '31390f6c-4cc9-424e-ade5-abe4b652e560'} +-------------------------------------------------------------------- +CVE-2025-26679 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows RPC Endpoint Mapper service (rpcrtremote.dll) +Function: wil_details_FeatureReporting_RecordUsageInCache + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (caused by erroneous reference counting / +out-of-range cache indexing) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine wil_details_FeatureReporting_RecordUsageInCache() maintains a +small two-DWORD cache that tracks whether a given feature identifier has +already been recorded. The second DWORD ( *(a2+1) ) contains a 1-bit +"present" flag (0x10) plus a 6-bit slot index (bits 5-10). The fast +path is intended to be used only when FeatureId (a3) is in the compact +range 320-383, so that the slot index (v6) is 0-63. + +Bug 1 – Missing upper bound / negative index +------------------------------------------- +Original code: + v6 = a3 - 320; + if ((int)a3 - 320 < 64) { // signed comparison + ...fast path... + } +Because the comparison is signed, every id < 384 satisfies the test, +including the entire range 8-319 where v6 becomes *negative*. The value +v6 is then multiplied by 32 and merged into *(a2+1), corrupting the slot +index field and possibly the 0x10 present bit for an unrelated feature. + +Bug 2 – Unconditional fallback increment +--------------------------------------- +After the (possibly bogus) fast path the function always executed + *(_DWORD *)(a1+8) = a3; + *(_DWORD *)(a1+4) = 1; + *(_DWORD *)(a1+12) = a4; +and returned. These assignments drive the slower +wil_details_FeatureReporting_* routines that bump the global reference +counter. Because the fallback ran even when the slot was already marked +"present", the reference count was incremented repeatedly, letting it +later reach zero too early. Concurrent cleanup code then freed the +backing structure while other threads still held pointers, producing a +classic use-after-free in the Endpoint Mapper process (SYSTEM-level). + +Combined result +--------------- +Out-of-range feature ids (8-319) or repeated legitimate ids (320-383) +allowed an attacker to poison the cache and desynchronize the reference +counter, eventually causing a free of a still-referenced object and +subsequent kernel-mode dereference. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Original +v6 = a3 - 320; +if ((int)a3 - 320 < 64) { // accepts negative values + v7 = *((_DWORD *)a2 + 1); + do { + v8 = (v7 & 0x10) != 0 && ((v7 >> 5) & 0x3F) == v6; + *(_DWORD *)(a1 + 16) = v8; // present flag + v9 = v7; + v7 = _InterlockedCompareExchange( + a2 + 1, + v7 ^ ((unsigned __int16)v7 ^ (unsigned __int16)(32 * v6)) & 0x7E0 | 0x10, + v7); + } while (v9 != v7); +} +*(_DWORD *)(a1 + 4) = 1; // fallback runs unconditionally +``` +```c +// Patched +if ((int)a3 - 320 >= 64) + goto LABEL_16; // reject out-of-range ids +... +if (!*(_DWORD *)(a1 + 16)) { // run fallback only on first hit +LABEL_16: + *(_DWORD *)(a1 + 8) = a3; + *(_DWORD *)(a1 + 4) = 1; + *(_DWORD *)(a1 + 12) = a4; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User process -> RPC call to Endpoint Mapper -> +Feature_Servicing_RPCFirewallManager_... -> +wil_details_FeatureReporting_RecordUsageInCache -> +cache corruption / refcount inflation -> +later cleanup frees still-referenced object -> +service dereferences freed memory (SYSTEM EoP). + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker able to invoke the Endpoint Mapper RPC +interface can supply crafted feature identifiers (8-319) or flood the +service with repeated identifiers in the 320-383 range. This poisons +the cache, drives the reference counter to zero prematurely, and triggers +a use-after-free inside the service’s process running as SYSTEM. + +Patch Description +-------------------------------------------------------------------- +1. Added a strict range check: the fast path is taken only when + 0 <= a3-320 < 64. Any other value falls back to the safe slow path. +2. Added a guard so the slow path executes only when the slot was not yet + present, preventing multiple increments of the same reference count. + +Security Impact +-------------------------------------------------------------------- +The vulnerability allowed a local attacker to elevate privileges to +SYSTEM by inducing a use-after-free in the Endpoint Mapper service. The +exploitability is high because only user-mode data is required and the +service runs with full system privileges. + +Fix Effectiveness +-------------------------------------------------------------------- +The additional bounds check eliminates the out-of-range write, and the +conditional fallback prevents reference-count inflation. Together these +changes remove the inconsistency that led to premature frees, fully +mitigating the reported UAF condition. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26679_rpcss.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26679_rpcss.dll.txt new file mode 100644 index 0000000..79c1411 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26679_rpcss.dll.txt @@ -0,0 +1,124 @@ +{'patch_store_uid': 'fbc8b878-8b09-4675-b50b-b286c5732325', 'cve': 'CVE-2025-26679', 'change_count': 100, 'kb': 'KB5055523', 'file': 'rpcss.dll', 'date': 1751829079.747125, 'confidence': 0.26} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft RPCSS (Rpc Endpoint Mapper) – function +GetInstalledPackageFullNameFromPackageFamilyName() in rpcss.dll. +The routine is reachable through several COM/RPC entry points that +allow normal user-level callers to ask the service for the full +package name that belongs to a package family. + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free / Double Free (CWE-416, CWE-415). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The function first asks an IUserTokenInternal object (parameter a1) +for an impersonation token via vtbl[96]. The returned kernel handle +is stored in local variable v30. A second vtbl entry (index 104) is +used to release that handle later. + +Old code kept a copy of the token handle only as a raw DWORD and +called the release method manually on *many* exit paths: + – after an early failure to obtain the token + – after each FindPackagesByPackageFamily() error + – again after the happy path just before returning success + +Because each branch performed its own clean-up it was possible to hit +logic that released the same handle twice: + 1. The first free happened when an error branch was taken that + called vtbl[104] and then executed the common LABEL_8 code. + 2. LABEL_8 itself also called vtbl[104] unconditionally, causing a + second free of the same token handle. + +A second CloseHandle() on the same kernel handle makes the value +available for re-use by the system. A local attacker who holds a +handle to a chosen object can win the race and re-allocate the same +handle value before rpcss uses it again, gaining a handle with higher +privileges inside the SYSTEM service – a classical use-after-free +leading to elevation of privilege. + +The defect exists only when a1 is non-NULL and the first +vtbl[96] call succeeded; otherwise no duplicate free occurs. The bug +is fully inside the service process; no special kernel primitives are +needed. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (simplified) +if (a1) { + hr = a1->GetToken(0, &hTok); // vtbl[96] + if (hr < 0) { + a1->CloseToken(hTok); // vtbl[104] 1st free + return hr; + } +} +... +err: +if (a1) + a1->CloseToken(hTok); // 2nd free – same value! +``` +```c +// after patch (simplified) +wil::scope_exit cleanup([&]{ + if (tokenObtained && pUser) + pUser->CloseToken(hTok); // executed exactly once +}); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client process -> RPC to Endpoint Mapper -> +GetInstalledPackageFullNameFromPackageFamilyName() + 1. Provide any package family string that forces + FindPackagesByPackageFamily() to fail with ERROR_INSUFFICIENT_BUFFER + (122) or any unexpected Win32 error. + 2. The function enters the 122/15701 branch, frees the token, then + jumps to LABEL_8 which frees it again. + 3. Attacker immediately re-opens a chosen handle value equal to the + freed one. + 4. Subsequent rpcss code operates on the attacker-controlled handle + under SYSTEM privileges. + +Attack Vector +-------------------------------------------------------------------- +Any authenticated local user can repeatedly call the public RPC +interfaces that end up in this helper routine. No special rights are +required beyond the capability to connect to the RPC Endpoint Mapper. +A precise timing attack allows the user to re-cycle the just-freed +handle value and obtain privileged access. + +Patch Description +-------------------------------------------------------------------- +The patch entirely removes the ad-hoc cleanup code and replaces it with +an automatic WIL scope-exit guard: + • Introduces a wil::scope_exit lambda (lambda_09c4ab…) capturing the + IUserTokenInternal pointer and the token handle. + • All explicit calls to vtbl[104] are deleted. + • Error reporting helpers changed from Return_Win32Msg() to + Return_HrMsg() but that is cosmetic. + +The guarded cleanup guarantees that the token handle is closed exactly +once – on whichever path the function returns – eliminating both +double-free and use-after-free conditions. + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could execute code in the context of +the RpcSs service (NT AUTHORITY\SYSTEM). Because RpcSs is a trusted +broker for all local RPC, this provides full privilege escalation on +the machine. + +Fix Effectiveness +-------------------------------------------------------------------- +The single-point scope-exit destructor ensures exactly-once release +semantics. No branches now contain manual frees, so double-free/UAF +is structurally impossible unless future modifications bypass the +scope-exit block. Static review of the patched function confirms that +hTok is never accessed after the destructor runs. The fix is therefore +considered complete for this code path. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26681_win32kbase.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26681_win32kbase.sys.txt new file mode 100644 index 0000000..ba99a13 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26681_win32kbase.sys.txt @@ -0,0 +1,124 @@ +{'date': 1751822645.238318, 'cve': 'CVE-2025-26681', 'change_count': 483, 'patch_store_uid': '89fe3a67-ea3b-4bdc-a937-95016fe04e59', 'confidence': 0.19, 'kb': 'KB5055523', 'file': 'win32kbase.sys'} +-------------------------------------------------------------------- +CVE-2025-26681 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kbase.sys – NtDCompositionSendDwmLpcMessage system call that +marshals a user supplied message (and optional handles) to the Desktop +Window Manager (DWM) through an LPC port. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (local privilege-escalation primitive) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch NtDCompositionSendDwmLpcMessage lacked the necessary +lifetime management for several kernel objects that are touched while +the user-mode caller, the current process, and the DWM process are all +active concurrently. In particular: + +1. No global DWM state lock was taken. While the routine was + preparing the LPC packet the DWM process object (struct _KPROCESS) + could be released by another thread, leaving a stale pointer that + was dereferenced later in the same call path. + +2. The routine duplicated up to two caller-supplied handles directly + into the DWM process but, if any later stage failed, the clean-up + path closed those duplicated handles *after* the address space of + the target process had already been detached. This closed the + remote handle table entry while DWM still believed the handle to be + valid, allowing subsequent DWM activity to access a freed kernel + object. + +3. Once the LPC call succeeded the code did not zero out the local + copies of the duplicated handles. If an early kernel APC aborted + the system call after the LPC but before return-to-user, the same + handles would be closed a second time when the thread unwound, + triggering a double close / UAF on the referenced object. + +The patch introduces three mitigations that collectively remove the +UAF window: + + • CheckOrAcquireDwmStateLock() is invoked and remembered via the + v24 flag so that the global composition state cannot change while + objects are in use. + + • ReferenceDwmProcess() now increments the DWM process refcount and + UserDereferenceDwmProcess() is called on all exits, guaranteeing + the _KPROCESS structure remains live for the entire routine. + + • After every successful ObDuplicateObject() the duplicated handle is + stored in v25; once the LPC round-trip completes the loop at the + end of the function overwrites each stored handle with zero so that + later clean-up logic cannot accidentally close it again. + +Because the old implementation could free the duplicated handle (or the +process object) while it was still reachable from a second thread, an +attacker running in a low-privilege user session could reliably convert +this into a local privilege escalation by arranging for the freed +kernel object to be re-allocated as a more privileged structure. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// NEW: take global lock +if ((unsigned __int8)CheckOrAcquireDwmStateLock()) + v24 = 1; +... +// NEW: explicit process reference +PROCESS = ReferenceDwmProcess(v7); +... +// NEW: scrub duplicated handles after LPC completes +for (j = 0; j < v12; ++j) + *((_QWORD *)&v25 + j) = 0; // prevents double close / UAF +``` +(The legacy version contained none of the above safeguards.) + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls NtDCompositionSendDwmLpcMessage with a crafted + 0x1C- or 0x20-byte message that contains one or two kernel handles. +2. System call duplicates those handles into the DWM process. +3. Attacker forces an error path (e.g. size > 0x20 or fake feature + code) that results in premature unwinding. +4. Routine closes the duplicated handle *after* the DWM process has + been detached, freeing the underlying object. +5. DWM continues to use the stale handle, creating a classic UAF that + can corrupt privileged kernel data. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. The only prerequisite is the ability to +invoke the win32k system call interface; no special privileges are +required beyond a GUI session. + +Patch Description +-------------------------------------------------------------------- +• Hard upper bound on user input: Size > 0x20 now returns STATUS_INVALID + PARAMETER. +• Global DWM state lock acquired for the entire duration. +• Reference counting added for _KPROCESS representing DWM. +• Successful handle duplications are tracked and zeroed before return + to prevent double closes. +• Extensive error handling added to close already-duplicated handles + *before* detaching from the target process. + +Security Impact +-------------------------------------------------------------------- +The bug provided a reliable kernel-mode use-after-free that could be +steered to overwrite or re-allocate freed objects with attacker-chosen +content, ultimately allowing arbitrary code execution in kernel +context. Under default Windows desktop configurations this translates +into a full SYSTEM privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation removes the UAF condition by enforcing proper +locking, reference counting, and handle lifetime rules, and by +sanitising user input size. No residual paths that operate on freed +objects can be reached, and attempts to repro the old crash now return +STATUS_INVALID_PARAMETER or STATUS_ACCESS_DENIED. The fix is therefore +assessed as complete and effective. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26681_win32kfull.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26681_win32kfull.sys.txt new file mode 100644 index 0000000..20b0df9 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26681_win32kfull.sys.txt @@ -0,0 +1,121 @@ +{'patch_store_uid': 'e061d126-b20f-43fa-89f5-f35342ceac9e', 'confidence': 0.11, 'change_count': 379, 'kb': 'KB5055523', 'file': 'win32kfull.sys', 'date': 1751822673.5201418, 'cve': 'CVE-2025-26681'} +-------------------------------------------------------------------- +CVE-2025-26681 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows kernel-mode driver win32kfull.sys – graphics +sub-component that manages per-device font tables +(DEVICE_PFTOBJ::bLoadFonts). + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free (CWE-416) caused by a multi-threaded race condition +that allows the same PDEV font table (PFF) to be created and freed +concurrently. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each graphics device (PDEV) keeps a list of PFF structures that +describe the fonts physically resident on that device. The helper +routine + DEVICE_PFTOBJ::bLoadFonts(HDEV) +was responsible for lazily loading this list: + 1. Take a semaphore locked at (SessionState + 96 + 4872). + 2. Search the per-PDEV hash for an existing PFF. + 3. If none was found allocate a new PFFMEMOBJ, call + PFFMEMOBJ::bLoadDeviceFontTable(), then re-take the semaphore and + insert the new object into the per-device hash list. + 4. Finally call PFFMEMOBJ::~PFFMEMOBJ(), which frees the underlying + allocation unless it had previously been "kept" with + vKeepIt(). + +The bug: the first semaphore is released *before* the expensive font +load takes place. Nothing prevents a second thread from entering the +function, observing that no PFF exists yet, and performing the same +allocation. When the two threads race back to the second semaphore +one of them wins and successfully inserts its PFF; the loser detects +that a PFF is now present and skips the insertion. Because the loser +never calls vKeepIt() its temporary PFFMEMOBJ is destroyed, freeing +the memory. However several fields from that freed allocation (most +importantly the FONTDIFF structures referenced via the PFF hash) were +already published globally during the load phase and are subsequently +reachable from kernel rendering paths. Any later dereference of those +pointers results in a use-after-free of kernel pool memory. + +Affected data: PFF objects linked through *(PFF **) (SessionState + +96 + 4872) and every indirect structure reachable from the freed +PFFMEMOBJ (font directory entries, glyph bitmaps, etc.). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch – relevant fragments +SEMOBJ<17>::SEMOBJ<17>(&v22, v7 + 4872); +v8 = DEVICE_PFTOBJ::pPFFGet(this, v20, &v21); +SEMOBJ<17>::vUnlock(&v22); +if (!v8) { + PFFMEMOBJ::PFFMEMOBJ((PFFMEMOBJ *)v19, ...); + if (v19[0] && PFFMEMOBJ::bLoadDeviceFontTable((PFFMEMOBJ *)v19,v20)){ + SEMOBJ<17>::SEMOBJ<17>(&v22, v7 + 4872); + if (!DEVICE_PFTOBJ::pPFFGet(this, v20, &v21)) // race window + PFFOBJ::bAddHash((PFFOBJ *)v19, 0); + SEMOBJ<17>::vUnlock(&v22); + } + PFFMEMOBJ::~PFFMEMOBJ((PFFMEMOBJ *)v19); // frees loser’s object +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode → GDI API that ends up in + GreDeviceLoadFonts → DEVICE_PFTOBJ::bLoadFonts() +Two or more caller threads pointing at the same HDEV run +concurrently: + Thread A and thread B both pass the first pPFFGet() check, allocate + separate PFFMEMOBJs, load the font tables and publish internal + pointers. Whichever thread loses the insertion race frees its + PFFMEMOBJ while shared pointers still reference it → UAF. + +Attack Vector +-------------------------------------------------------------------- +A local, low-privilege process opens a printer/display device context +and spawns two threads that simultaneously invoke a font-triggering +GDI call (e.g., AddFontResourceEx/StartDoc). With careful timing the +racing path frees pool memory that remains reachable from the global +PFF hash. The attacker can then reclaim the freed pool with +controlled data and gain arbitrary kernel read/write leading to +elevation of privilege. + +Patch Description +-------------------------------------------------------------------- +The patched routine signature becomes + bLoadFonts(HDEV, uint NumFonts) +Major hardening steps: +1. A per-device GOT_FONTS bit (0x40) in the PDEV flag field is set + atomically using InterlockedCompareExchange. The first thread that + sets this bit becomes the *sole* loader; later callers bail out + early. +2. The font count is now supplied explicitly (a3) so that secondary + threads do not have to re-query device state while the first thread + is loading. +3. Additional *Feature_H2E_WPA3SAE* checks and early exits are added + to allow safe disablement. +4. Ref-counting logic stays, but PFFMEMOBJ is only destroyed when the + object was never inserted, removing the double-free window. + +Security Impact +-------------------------------------------------------------------- +Before the patch an authenticated local attacker could reliably trigger +kernel-mode use-after-free, corrupting pool memory and executing +arbitrary code in ring-0, thereby escalating to SYSTEM. The flaw is +rated Elevation of Privilege; remote exploitation is not required. + +Fix Effectiveness +-------------------------------------------------------------------- +Serialisation of font loading via an interlocked GOT_FONTS flag closes +the race, guaranteeing that at most one PFFMEMOBJ is created per +PDEV. Subsequent callers operate on the already-initialised object +and no longer touch freed memory. No alternative UAF path was +identified in the updated code, so the fix is considered effective. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26686_tcpip.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26686_tcpip.sys.txt new file mode 100644 index 0000000..36e6f42 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26686_tcpip.sys.txt @@ -0,0 +1,123 @@ +{'date': 1751820822.6490295, 'patch_store_uid': '052ca79a-9638-4c9a-8db4-c867fa67de76', 'confidence': 0.19, 'kb': 'KB5055523', 'cve': 'CVE-2025-26686', 'change_count': 2, 'file': 'tcpip.sys'} +-------------------------------------------------------------------- +CVE-2025-26686 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows kernel networking driver (tcpip.sys) – IPv4/IPv6 +routing/neighbor management routine IppRedirectPath. + +Vulnerability Class +-------------------------------------------------------------------- +Race-condition / use-after-free due to improper locking and reference +handling (also leads to double-dereference). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +IppRedirectPath is invoked when the stack processes an ICMP Redirect or +similar routing hint. The routine locates the affected PATH structure, +fetches the corresponding NEIGHBOR object and, when required, replaces +the next-hop pointer inside the PATH. + +Pre-patch logic manipulates these shared structures manually: + 1. It bumps the reference count of the new NEIGHBOR via + IppReferenceNeighborEx(). + 2. It acquires a *different* scalable lock that protects the ROUTE + table, not the PATH itself. + 3. It calls IppSetNextHopInPathUnderLock() to overwrite the PATH + field, then releases the lock. + 4. On many error paths it dereferences the same objects again. + +Two fundamental problems follow from this design: + +• Missing object-specific lock + The PATH object’s own spin lock is never held while the next-hop + pointer is replaced. Concurrent readers obtaining the PATH from the + route cache can race and obtain a pointer to the *old* NEIGHBOR after + it has already been dereferenced (and possibly freed), yielding a + dangling pointer that is subsequently used for packet processing. + +• Unbalanced reference handling + The code path labelled LABEL_25/LABEL_29 dereferences the PATH twice + when v7 == Path (this happens whenever IppRouteToDestinationInternal + returns the original path). The double decrement drives the PATH + refcount to zero prematurely, making the structure re-usable while it + is still in active use by other CPUs. + +Both issues allow controlled kernel memory corruption. Because the +routine is reachable through attacker-supplied ICMP Redirect packets, +the flaw can be exploited remotely to execute arbitrary code in kernel +mode. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (simplified) +v21 = IppRouteToDestinationInternal(..., &v26); // v26 == new PATH +... +NextHopFromPath = IppGetNextHopFromPath(v26); +... +IppReferenceNeighborEx(NextHopFromPath, 1); +RtlAcquireScalableWriteLock(**(...)+832,&v30); // wrong lock +IppSetNextHopInPathUnderLock(Path, NextHopFromPath,0,v23); +KeReleaseInStackQueuedSpinLock(&v30); +... +IppDereferenceNeighbor(v27); // ok +if (v7) IppDereferencePath(v7); // may equal Path +... +IppDereferencePath(Path); // second deref -> UAF +``` + +```c +// post-patch +IppUpdateNextHop(Path, NextHopFromPath); // atomic helper +... +IppDereferencePath(v7, ...); // balanced +... +IppDereferencePath(Path, ...); // single final deref +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends a crafted ICMP Redirect that forces the stack to call + IppRedirectPath(). +2. Routine finds existing PATH (Path) and concurrent traffic keeps a + reference to it. +3. Redirect forces a next-hop change; old code swaps pointer without + proper PATH lock and over-releases references. +4. Parallel CPU accesses freed PATH/NEIGHBOR memory, leading to heap + corruption and controlled RIP/RSP when corrupted object is later + dereferenced. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated remote attacker on the same network segment sends a +sequence of forged ICMP Redirect (IPv4) or Router Advertisement (IPv6) +messages crafted to race the PATH update logic. No local privileges are +required. + +Patch Description +-------------------------------------------------------------------- +• Introduces new helper IppUpdateNextHop() that acquires the correct + PATH-level lock, swaps the next-hop pointer atomically, and guarantees + symmetrical reference counting. +• Removes hand-rolled RtlAcquireScalableWriteLock/KeRelease pair. +• Splits previously aliased variables ensuring each structure pointer + (INTERFACE, PATH, NEIGHBOR) is kept in its own register. +• Adds feature gating code that rejects certain invalid next-hop + pointers before they are referenced. + +Security Impact +-------------------------------------------------------------------- +Pre-patch race enables remote, kernel-mode use-after-free. Successful +exploitation gives an attacker arbitrary code execution with SYSTEM +privileges (network-to-kernel RCE). Denial-of-service is also trivial. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch centralises pointer exchange in a vetted helper that holds the +correct lock and balances references, eliminating the observed race and +double dereference. No residual paths to update next-hop remain in this +routine; however, other call-sites must use the same helper to guarantee +full mitigation. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26687_win32kfull.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26687_win32kfull.sys.txt new file mode 100644 index 0000000..972a7ab --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26687_win32kfull.sys.txt @@ -0,0 +1,125 @@ +{'kb': 'KB5055523', 'patch_store_uid': 'e061d126-b20f-43fa-89f5-f35342ceac9e', 'cve': 'CVE-2025-26687', 'date': 1751820864.8783877, 'confidence': 0.18, 'change_count': 379, 'file': 'win32kfull.sys'} +-------------------------------------------------------------------- +CVE-2025-26687 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kfull.sys – raw-input subsystem (HIDDATA handling) +Affected paths include: + • NtUserGetRawInputData() + • FlushPostedRawInputAndUnlinkThisOne() + • support helpers (UnlinkHidData, GetRawInputData_NoUserCrit) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (time-of-check / time-of-use race on + tagHIDDATA list nodes) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Background +Each GUI thread owns two single-linked lists that store tagHIDDATA +structures (raw HID packets): + a1+72 – per-cpu SList used by interrupt/APC producers + a1+73 – normal linked list consumed by user-mode via the + NtUserGetRawInputData API. + +Old behaviour (FlushPostedRawInputAndUnlinkThisOne) +1. The routine flushed the SList with + ExpInterlockedFlushSList(a1+72) + returning a forward-linked chain. +2. It manually *reversed* that chain (v7) and then spliced it at the + *tail* of the consumer list by walking every node until the last + Alignment pointer became NULL. +3. It subsequently walked the whole list again, looking for a caller + supplied node ‘a2’; once found it performed: + *v10 = a2->Next; + a2->Next = NULL; + to unlink the element and returned the freed pointer back to the + caller. + +Missing synchronisation +• The two operations above are executed outside any user-mode or kernel + critical section. +• While FlushPostedRawInput… is running, another thread in the same + process can call NtUserGetRawInputData(), which *decrements the + reference count* of the same HIDDATA and may free it via + GRID_CopyHidData. +• Because the unlink loop dereferences a2 *after* it is removed from the + list, the memory can already be recycled, leading to a dangling + pointer write (UAF). +• An attacker controlling the consumer thread can race the producer + (e.g. by flooding WM_INPUT messages) to create an arbitrary kernel + R/W primitive and escalate privileges. + +Side effects visible in the diff +• NtUserGetRawInputData previously searched for the HRAWINPUT in + thread-private lists without holding any lock; the same race allowed + it to obtain an already-freed pointer. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v10 = (tagHIDDATA**)&a1[73]; +while (1) { + v13 = *v10; // unprotected deref + if (!v13) return 0; // list empty + if (v13 == a2) break; + v10 = (tagHIDDATA**)((char*)v13 + 32); +} +*v10 = (tagHIDDATA*)*((QWORD*)a2 + 4); // writes into possibly freed mem + +// after +for (i = a1+73; i->Alignment; i=(union _SLIST_HEADER*)(i->Alignment+32)); +i->Alignment = v7; // safe splice +return UnlinkHidData((tagTHREADINFO*)a1, a2, a3); // centralised, locked +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker creates two threads in the same GUI process. +2. Thread A continuously issues raw-input reads + NtUserGetRawInputData(handle) + causing rapid alloc/free of tagHIDDATA. +3. Thread B calls any win32k API that ends up in + FlushPostedRawInputAndUnlinkThisOne() + (e.g. DestroyWindow, message pump cleanup). +4. Race window lets Thread A free ‘a2’ while Thread B is still inside + the unlink loop ⇒ UAF and arbitrary kernel memory overwrite. + +Attack Vector +-------------------------------------------------------------------- +Local, low-integrity user. No special privileges are required; only the +ability to create a GUI thread and issue raw-input APIs. + +Patch Description +-------------------------------------------------------------------- +1. Re-implemented FlushPostedRawInputAndUnlinkThisOne(): + • Uses two temporaries (v7/v8) to reverse the flush list safely. + • Splices the list by searching for the first NULL Alignment pointer + instead of walking *live* HIDDATA nodes. + • Delegates unlinking to new helper UnlinkHidData() that acquires the + proper push-lock / reference count. +2. NtUserGetRawInputData() now calls + GetRawInputData_NoUserCrit() + which internally performs the search under the same lock, instead of + re-implementing the walk. +3. Ancillary clean-ups in RFONTOBJ and HDEV paths remove redundant list + manipulation that could touch freed objects. + +Security Impact +-------------------------------------------------------------------- +Successful exploitation yields kernel-mode read/write of freed memory +inside win32k, allowing Local Elevation of Privilege and potential +sandbox or session escape. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable pointer arithmetic and unlocked writes are gone; all list +operations are now funnelled through UnlinkHidData / +GetRawInputData_NoUserCrit which hold the win32k push-lock and maintain +reference counts. No paths remain where tagHIDDATA can be freed while a +raw pointer to it is still being dereferenced, effectively closing the +UAF window. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26688_vhdmp.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26688_vhdmp.sys.txt new file mode 100644 index 0000000..33a4768 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-26688_vhdmp.sys.txt @@ -0,0 +1,123 @@ +{'date': 1751820800.6117198, 'file': 'vhdmp.sys', 'patch_store_uid': '6a876d1b-36cd-4e02-9ac4-9045a84bd776', 'confidence': 0.13, 'change_count': 3, 'cve': 'CVE-2025-26688', 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-26688 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Virtual Hard-Disk mini-port driver (vhdmp.sys). +The faulty code is located in the mirror-creation helper +VhdmpiInitializeMirror() and reached through the user-accessible +IOCTL path that initialises a VHD “mirror” file. A secondary, +non-security-relevant change is present in VhdmpiDeleteVirtualDisk(). + + +Vulnerability Class +-------------------------------------------------------------------- +Stack-based Buffer Overflow (CWE-121). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Input buffer a3 (size a4 bytes) is supplied by the caller and has the +following layout: + DWORD OffsetToPath; // *a3 + DWORD PathLengthBytes; // a3[1] + BYTE Flags[?] // *(BYTE *)a3+8 … +The driver copies this data into a local UNICODE_STRING that lives on +the kernel stack (variable v66/v68): + + *((QWORD *)&v66 + 1) = (char *)a3 + *a3; // Buffer + LOWORD(v66) = *((WORD *)a3 + 2); // Length + WORD1(v66) = v66; // MaximumLength = Length + +Before the patch the only validation was + if (a4 < 0xC || OffsetToPath < 0xC || PathLength == 0 || + a4 < OffsetToPath || a4-OffsetToPath < PathLength) fail; +Critically, no check assured that PathLengthBytes is a multiple of two +or that a terminating L"\0" would fit. If the caller supplies an odd +length value, the string is treated as WCHAR[PathLength/2] by +sub-routines such as VhdmpiCreateBackingStore() and lower Rtl‐string +helpers. Those helpers append a wide NUL, writing two bytes past the +end of the stack buffer v66. Because v66 sits next to other saved +registers and frame data, this off-by-two write corrupts the stack +frame and can be leveraged to hijack execution when the function +returns, yielding kernel-mode code execution. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch excerpt (vhdmp.sys) +if (a4 < 0xC || (v19 = *a3, *a3 < 0xC) || + (v14 = a3[1]) == 0 || a4 < v19 || a4 - v19 < v14) + goto fail; +... +*((_QWORD *)&v66 + 1) = (char *)a3 + *a3; // Buffer +LOWORD(v66) = *((_WORD *)a3 + 2); // Length (possibly odd) +WORD1(v66) = v66; // MaximumLength +``` + +```c +// patched validation +if (a4 < 0xC) goto fail; +oddSafe = Feature_IsEnabled(); +pathOff = *a3; +pathLen = a3[1]; +if (!oddSafe) { + if (pathOff < 0xC || pathLen == 0) goto fail; +} else { + if (pathOff < 0xC || pathLen == 0 || (pathLen & 1)) goto fail; // NEW +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens a VHD handle and sends the IOCTL that eventually + calls VhdmpiInitializeMirror(). +2. The supplied buffer contains OffsetToPath = 0x10, PathLength = 0x15 + (odd) followed by crafted data. +3. Old validation accepts the buffer. +4. UNICODE_STRING is built on the stack with Length = 0x15. +5. Rtl-helpers append a wide NUL => write 2 bytes past v66. +6. On function epilogue the corrupted stack frame is used, allowing the + attacker to redirect execution in kernel space -> privilege + escalation. + + +Attack Vector +-------------------------------------------------------------------- +Any local, authenticated user who can open the VHDMP device +(e.g. through the public CreateFileW("\\.\GlobalRoot\Device\Vhdmp...") +interface) can send the mirror-initialisation IOCTL with a crafted +parameter block to trigger the overflow and run arbitrary code in +kernel context. + + +Patch Description +-------------------------------------------------------------------- +1. Added a feature-gated validation branch that rejects input when + PathLengthBytes is zero OR not aligned to WCHAR (length & 1). +2. Refactored error-handling paths so the function aborts before the + UNICODE_STRING is built. +3. Other changes are cosmetic (trace IDs, label renames) or relate to + DvRundownWorkQueue in VhdmpiDeleteVirtualDisk and are not + security-relevant. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a user-mode attacker could corrupt the kernel stack +from ring-3, leading to arbitrary execution in the Windows kernel and +full local elevation of privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added length-alignment check completely blocks the malformed +buffers required for exploitation, and it is performed before the +unsafe UNICODE_STRING is created. Provided no alternate path bypasses +Feature_2119187769__private_IsEnabledDeviceUsageNoInline(), the patch +is effective in preventing the stack overflow. + diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27467_windows.media.playback.mediaplayer.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27467_windows.media.playback.mediaplayer.dll.txt new file mode 100644 index 0000000..d5d35f1 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27467_windows.media.playback.mediaplayer.dll.txt @@ -0,0 +1,117 @@ +{'patch_store_uid': 'dde755f3-6cfd-4bb7-86ec-3f14fa2eea1c', 'change_count': 1, 'date': 1751828834.5726345, 'kb': 'KB5055523', 'file': 'windows.media.playback.mediaplayer.dll', 'confidence': 0.22, 'cve': 'CVE-2025-27467'} +-------------------------------------------------------------------- +CVE-2025-27467 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.media.playback.mediaplayer.dll – function +MediaPlayerImpl::WaitForVBlankLoop(). The code operates in the +Digital-Media playback stack and interacts with the DXGI IDXGIOutput +interface to wait on vertical-blank events. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The loop maintains two raw interface pointers: + v6 – process-wide cache of IDXGIOutput* (owner, freed with + Microsoft::WRL::ComPtr::<InternalRelease>). + v4 – per-iteration alias of v6, **copied without AddRef**. + +Original logic: + 1. v4 = v6 // no AddRef + 2. if (!v6) { + InternalRelease(&v6); // may free object + GetDefaultDXGIOutput(&v6); // may fail, v6==NULL + v4 = v6; // may stay NULL + } + 3. ((IDXGIOutput*)v4)->WaitForVBlank(v4); // unconditional call + 4. if call failed, InternalRelease(&v6). + +If GetDefaultDXGIOutput() fails (for instance when no suitable +adapter/output exists or has just been hot-unplugged) the branch leaves +v4 pointing at the *previous* object that was just passed to +InternalRelease(). When the reference count hit zero, the underlying +IDXGIOutput is destroyed. The unconditional dereference at step 3 +therefore operates on freed memory – a classic Use-After-Free. + +Because IDXGIOutput vtbl entries ultimately live inside the DirectX +kernel driver stack, control of the freed memory can be leveraged to +redirect execution flow inside a medium-integrity media process and +escalate privileges locally. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v4 = v6; +if (!v6) { + InternalRelease(&v6); + GetDefaultDXGIOutput(&v6); + v4 = v6; +} +if (((int (__fastcall *)(IDXGIOutput*))v4->lpVtbl->WaitForVBlank)(v4) < 0) +{ + InternalRelease(&v6); + Sleep(0x10); +} + +// after +if ( !v6 && + (InternalRelease(&v6), // may delete object + GetDefaultDXGIOutput(&v6), + (v4 = v6) == 0) // bail out if still NULL + || ((int (__fastcall *)(IDXGIOutput*))v4->lpVtbl->WaitForVBlank)(v4) < 0 ) +{ + InternalRelease(&v6); + Sleep(0x10); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker arranges for MediaPlayerImpl::WaitForVBlankLoop() to run + (e.g., start media playback). +2. Force the last cached IDXGIOutput to be freed (device removal, + display-adapter surprise removal, or custom DXGI hook returning + DXGI_ERROR_DEVICE_REMOVED). +3. Ensure subsequent GetDefaultDXGIOutput() fails, leaving v6==NULL. +4. Function dereferences stale v4 pointer, executing code from freed + memory. + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to interact with the desktop stack can +unplug / re-plug displays or use crafted DXGI shims to control the +lifetime of IDXGIOutput objects. When the UAF is hit the attacker can +reallocate the freed chunk with attacker-controlled data, obtain code +execution in the media process, and elevate privileges. + +Patch Description +-------------------------------------------------------------------- +The fix re-orders the control flow so that WaitForVBlank() is *only* +invoked when a valid IDXGIOutput pointer is present: + • Releases the old object and attempts to reacquire a new one *inside* + the same Boolean expression. + • Immediately checks whether the reacquisition failed. If it did, the + left side of the expression is true and short-circuit evaluation + prevents the call to WaitForVBlank(), eliminating the UAF + dereference. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could execute code with the privileges of +any process using MediaPlayerImpl::WaitForVBlankLoop(), allowing local +Elevation of Privilege. Impact scope is at least the media playback +service; full system compromise is possible but unconfirmed. + +Fix Effectiveness +-------------------------------------------------------------------- +The conditional short-circuit removes the only path that dereferenced a +freed IDXGIOutput pointer, effectively closing the UAF. No residual +paths to the freed object are evident in the modified routine. A full +code audit for similar AddRef/Release imbalances elsewhere in the +Digital-Media stack is still recommended. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27469_dcsvc.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27469_dcsvc.dll.txt new file mode 100644 index 0000000..f6006ba --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27469_dcsvc.dll.txt @@ -0,0 +1,119 @@ +{'date': 1751820861.3129308, 'patch_store_uid': '77df0a6c-a202-4341-b125-0e656b9fc6b7', 'file': 'dcsvc.dll', 'change_count': 11, 'kb': 'KB5055523', 'cve': 'CVE-2025-27469', 'confidence': 0.21} +-------------------------------------------------------------------- +CVE-2025-27469 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows dcsvc.dll – Declared-Configuration service, function +PreProcessBulkTemplateSettings(). The code executes inside the +svchost-hosted Device-Management (DM) service and is reachable when +processing bulk DM templates that arrive through LDAP/MDM channels. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-400 – Uncontrolled Resource Consumption / Denial of Service. +A remotely supplied integer is used as a loop bound that drives dynamic +allocation without any upper-bound validation, allowing an attacker to +force the service to consume arbitrary amounts of memory and CPU. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The service first calls into the orchestrator engine which returns a + WCHAR string ( lpMem ) containing the XML variable + "@#InstanceAmount#" for the bulk template. + +2. The value is converted to an integer with + wcstol( lpMem , &EndPtr , 10 ); + (variable v16 in the original code, plUbound in the patch). + No range checking is done; any positive 32-bit number is accepted. + +3. When the parsed number is >1 the function executes the following + loop: + for ( i = InstanceAmount-1 ; i > 0 ; --i ) + new DCCSP(); // 0x98-byte object + push_back( vectorOfCSP , sp ); + Each iteration allocates two ref-count objects (std::_Ref_count_obj + and DCCSP) and appends a shared_ptr into the vector located at + a2+160. There is no upper limit, so a crafted value (e.g. 0x7fffffff) + leads to billions of allocations and ultimately process termination + due to out-of-memory or heap exhaustion. + +4. Prior to the patch, if the _RTDynamicCast_0 to DCCSP failed the code + fell through to label 78 and immediately dereferenced the nullptr + ( *(_QWORD *)(v30+24) ). This null-deref terminates the thread, + guaranteeing service outage even before memory is exhausted. + +5. Because PreProcessBulkTemplateSettings() is executed while holding + the service RPC thread, crashing or stalling it renders the whole DM + stack unavailable, resulting in an LDAP-triggered denial of service + (DoS). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch +v16 = wcstol((const wchar_t *)lpMem, &EndPtr, 10); +if (v16 > 1) { + v17 = v16 - 1; // attacker-controlled loop + do { + v18 = _RTDynamicCast_0(**((QWORD**)a2+20), ...); + v19 = operator new(0x98); + ... // new DCCSP, push_back() + } while (--v17); +} +``` + +```c +// after patch (excerpt) +plUbound = wcstol((const wchar_t *)lpMem,&EndPtr,10); +if (*EndPtr) { return E_UNEXPECTED; } +if (!lpMem) { return E_OUTOFMEMORY; } +// Feature gate & extra error paths added before allocation +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Network (LDAP bulk template) → DeviceManagement-DMF → +PreProcessBulkTemplateSettings() + → orchestrator returns InstanceAmount string + → wcstol() parses it + → unchecked loop allocates DCCSP objects + → memory/CPU exhaustion or immediate NULL-deref → service crash. + +Attack Vector +-------------------------------------------------------------------- +A remote, unauthenticated attacker sends a crafted bulk template that +contains "@#InstanceAmount#" set to an extremely large numeric value. +Because the string is consumed via LDAP/MDM policy channels, no local +privilege is required. The service allocates until resources are +exhausted or crashes on the null dereference. + +Patch Description +-------------------------------------------------------------------- +The patch introduces multiple defensive changes: +1. Early parameter validation – lpMem NULL now returns + E_INVALIDARG instead of continuing. +2. The parsed integer is validated by checking *EndPtr and other + feature-flag-protected conditions; execution aborts on malformed or + empty data before any allocation. +3. All heap objects created through shared_ptr wrappers are now tracked + with wil::unique_any / std::wstring::_Tidy helpers that guarantee + release on every exit path, preventing additional leaks. +4. Numerous new error exits (Return_Hr) ensure the function returns + before the unbounded allocation loop when data is invalid. + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could crash the Device-Management service +or exhaust system memory, resulting in denial of service for LDAP / MDM +operations and any dependent management tasks. No authentication or +local code execution was required. + +Fix Effectiveness +-------------------------------------------------------------------- +Input is now validated and the dangerous code paths are skipped when +lpMem is missing, non-numeric, or feature flags are disabled. Because +allocation is no longer performed on attacker-supplied extreme values +and all exit paths free temporary buffers, uncontrolled resource +consumption is mitigated and the service remains available. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27469_ntdsai.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27469_ntdsai.dll.txt new file mode 100644 index 0000000..5fc9b95 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27469_ntdsai.dll.txt @@ -0,0 +1,120 @@ +{'date': 1751820879.7073095, 'patch_store_uid': 'a85e12c7-310f-4444-a434-b5b808f3b783', 'confidence': 0.27, 'change_count': 21, 'cve': 'CVE-2025-27469', 'file': 'ntdsai.dll', 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-27469 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Active Directory Domain Services – LDAP server implementation in +ntdsai.dll (functions LDAP_REQUEST::GrowReceive, ProcessAdminLimits, +ResetDefaultLimits and LDAP_CONN::BatchRequest) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-400: Uncontrolled Resource Consumption (Denial-of-Service via +unbounded memory allocation) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The receive path for an LDAP connection grows its per-request receive +buffer through LDAP_REQUEST::GrowReceive(ULONG NewSize). + +PRE-PATCH behaviour +• The routine compared the requested size against the global + LdapMaxReceiveBuffer (default 10 MB) for every connection, bound or + not. +• If the buffer in use was the stack-embedded 4 KB area the code + executed: + v8 = DSAllocAux(NewSize,…); + with NewSize chosen as either the caller supplied value or + CurrentSize+4096, whichever was larger but still <= + LdapMaxReceiveBuffer. +• For already heap-backed buffers DSReallocAux() was used and the size + doubled until it also reached LdapMaxReceiveBuffer. +• There was no separate cap for the *pre-authentication* phase. An + unauthenticated client could therefore force allocations up to + LdapMaxReceiveBuffer (or up to 4 GB on some code paths) simply by + sending oversized BER encoded PDUs before BIND completed. +• Multiple such connections quickly exhaust paged-pool/heap memory and + stop the DC from servicing further requests (= DoS). + +PATCH behaviour (feature flag 0x824731960) +• ResetDefaultLimits now initialises a new global + LdapMaxPreAuthReceiveBuffer to 0x40000 (256 KB). +• ProcessAdminLimits also clamps this value to 0x80000000 and exposes + it to SASL so that admin policy cannot raise it beyond 2 GB. +• GrowReceive detects whether the connection is authenticated via + LDAP_CONN::IsBound(): + maxAllowed = IsBound ? LdapMaxReceiveBuffer + : LdapMaxPreAuthReceiveBuffer; + Every subsequent size comparison (growth, caller supplied a2 etc.) + uses maxAllowed, guaranteeing that an unauthenticated connection can + never obtain a buffer larger than LdapMaxPreAuthReceiveBuffer. +• The rest of the logic is unchanged; once the client successfully + binds, the higher (administrator configurable) limit applies. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (simplified) +if (requestBuf == stackBuf) { + if (NewSize > LdapMaxReceiveBuffer) return FAIL; + p = DSAllocAux(NewSize, TAG); + memcpy(p, stackBuf, used); +} +... +// post-patch +IsBound = LDAP_CONN::IsBound(conn); +maxCap = IsBound ? LdapMaxReceiveBuffer + : LdapMaxPreAuthReceiveBuffer; +if (requestBuf == stackBuf) { + if (NewSize > maxCap) return FAIL; + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote unauthenticated client opens an LDAP TCP session. +2. Sends a crafted BER element whose length field advertises an overly + large payload size. +3. ntdsai!LDAP_CONN::BatchRequest → ..GrowReceive(AdvertisedLen). +4. Before patch: AdvertisedLen passes the single global cap, DSAllocAux + allocates AdvertisedLen bytes. +5. Repeat on many sockets → memory exhaustion → LSASS/NTDS service + crash or unresponsiveness. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker sending oversized LDAP packets to port +389/636. No credentials or special permissions are required. + +Patch Description +-------------------------------------------------------------------- +• Introduced new configurable limit LdapMaxPreAuthReceiveBuffer; default + 256 KB, enforceable upper bound 2 GB. +• GrowReceive now selects per-connection cap based on + LDAP_CONN::IsBound(). +• ResetDefaultLimits initialises the new limit; ProcessAdminLimits + validates administrator overrides. +• Additional WPP logging and feature-flag plumbing were added but do + not affect the fix logic. + +Security Impact +-------------------------------------------------------------------- +Before the fix, each unauthenticated connection could claim up to the +full LdapMaxReceiveBuffer (default 10 MB, potentially higher via admin +configuration). Hundreds of parallel connections could therefore +exhaust heap/paged pool, leading to denial of service of the Domain +Controller. After the fix, pre-auth connections are constrained to +256 KB, reducing worst-case memory use by ~40× and eliminating the easy +DoS condition. + +Fix Effectiveness +-------------------------------------------------------------------- +The allocation path is now guarded by a hard cap that applies *before* +authentication completes. Attempts to exceed 256 KB cause GrowReceive +to fail, the request is rejected, and no large allocation occurs. Once +bound, allocations are still allowed up to the (administrator-controlled) +higher limit, preserving normal functionality. The patch fully +mitigates the uncontrolled resource consumption vector described. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27471_ks.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27471_ks.sys.txt new file mode 100644 index 0000000..239da12 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27471_ks.sys.txt @@ -0,0 +1,112 @@ +{'cve': 'CVE-2025-27471', 'confidence': 0.11, 'file': 'ks.sys', 'date': 1751822661.8651147, 'kb': 'KB5055523', 'change_count': 16, 'patch_store_uid': '70e3bf67-1567-411c-af77-267a00e80496'} +-------------------------------------------------------------------- +CVE-2025-27471 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kernel Streaming driver (ks.sys) – MDL-cache handling, post- +probe processing, queue transfer, and KsProbeStreamIrp routines. + +Vulnerability Class +-------------------------------------------------------------------- +Input-validation error leading to NULL-pointer dereference / kernel +crash (Denial-of-Service). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Streaming IRPs contain a sequence of 0x38-byte KSPMDLCACHED buffer +headers. Offset +0x30 of each header holds a 64-bit data pointer; +offset +0x30 of dword[12] (flags) contains bits 0x8000 / 0x10000 / +0x18000 that indicate the pointer refers to user memory, device +memory, or a special 4-k page. + +Before the patch three different paths – + • CKsMdlcache::MdlCacheHandleThunkBufferIrp + • KsProbeStreamIrp + • CKsQueue::TransferKsIrp +validated the pointer only when an internal diagnostic switch +("Feature_2092524859__private_IsEnabledDeviceUsageNoInline") was +enabled. With the switch disabled (default on production builds) +a header could set 0x8000/0x18000 while leaving the data pointer +NULL. + +The code then executed + IoAllocateMdl(ptr /*==NULL*/, length, …) +producing an MDL whose StartVa is 0. Later stages tried to map or +copy the payload: + MappedSystemVa = MmMapLockedPagesSpecifyCache(mdl,…); + memmove(dst, *(void **)header+0x28, length); +Dereferencing address 0 or mapping page frame 0 causes a bugcheck +(PAGE_FAULT_IN_NONPAGED_AREA or IRQL_NOT_LESS_OR_EQUAL), crashing the +system and denying service to all users. No privileges are required +beyond the ability to send a crafted KS streaming request. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before (MdlCacheHandleThunkBufferIrp) +if ((UserHdr[12] & 0x8000) && + Feature_2092524859__private_IsEnabledDeviceUsageNoInline() && + !*((uint64_t*)UserHdr + 5)) + goto error; +// after – feature gate removed, check is unconditional +if (!*((uint64_t*)UserHdr + 5)) + return STATUS_INVALID_PARAMETER; +``` +```c +// before (KsProbeStreamIrp) +if ((Flags & 0x18000) && + Feature_2092524859__private_IsEnabledDeviceUsageNoInline() && + !DataPtr) + ExRaiseStatus(STATUS_INVALID_PARAMETER); +// after – always enforced +if ((Flags & 0x18000) && !DataPtr) + ExRaiseStatus(STATUS_INVALID_PARAMETER); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends crafted KS IRP with at least one 0x38-byte header. +2. Header sets Flags bit 0x8000 or 0x18000 and Length > 0. +3. Data pointer field is NULL. +4. CKsMdlcache::MdlCacheHandleThunkBufferIrp / KsProbeStreamIrp calls + IoAllocateMdl(NULL,…), building MDL with StartVa==0. +5. Subsequent MmMapLockedPagesSpecifyCache or memmove touches the NULL + address in kernel context ⇒ bugcheck ⇒ system reboot. + +Attack Vector +-------------------------------------------------------------------- +Any local or network client that can issue IOCTL_KS_READ_STREAM or +similar streaming requests to ks.sys can supply the malformed header. +No authentication or code-execution privilege is required – only the +ability to open the KS filter pin. + +Patch Description +-------------------------------------------------------------------- +• Removed all feature-flag gates around pointer validation. +• Added unconditional NULL-pointer checks in + - CKsMdlcache::MdlCacheHandleThunkBufferIrp + - KsProbeStreamIrp + - CKsMdlcache::MdlCacheProcessPostProbeIrp + - CKsQueue::TransferKsIrp +• Early returns STATUS_INVALID_PARAMETER when pointer is NULL. +• Minor refactors (variable renames, tracing GUIDs) but no functional + impact on the fix. + +Security Impact +-------------------------------------------------------------------- +Unauthenticated attackers could reliably crash Windows systems, +causing a complete denial of service. No information disclosure or +privilege escalation is implied, but the crash interrupts all running +workloads. + +Fix Effectiveness +-------------------------------------------------------------------- +After the patch, every path that handles a descriptor with 0x8000 / +0x18000 / 0x10000 now refuses the IRP when the associated data pointer +is NULL. Because MDLs are never created for address 0, subsequent +mapping/dereference cannot occur, eliminating the crash condition. +Static inspection shows no remaining path bypassing the new check and +no new attack surface introduced. + diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27471_mskssrv.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27471_mskssrv.sys.txt new file mode 100644 index 0000000..a94bcd5 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27471_mskssrv.sys.txt @@ -0,0 +1,131 @@ +{'file': 'mskssrv.sys', 'confidence': 0.19, 'patch_store_uid': 'fdb6ca27-1793-4cda-8932-cb2765555e42', 'change_count': 1, 'cve': 'CVE-2025-27471', 'kb': 'KB5055523', 'date': 1751822601.5140274} +-------------------------------------------------------------------- +CVE-2025-27471 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows kernel-mode streaming driver mskssrv.sys – routine +wil_details_FeatureReporting_RecordUsageInCache(), reached from the +SrvDispatchIoControl IOCTL handling path. + +Vulnerability Class +-------------------------------------------------------------------- +Race condition / improper locking of a shared in-kernel statistics +cache (CWE-667/591). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The driver keeps a two-DWORD per-process cache that records how often a +particular "feature id" has already been reported to the Windows +feature-usage telemetry service. The function receives + a1 – output buffer to be filled + a2 – pointer to the volatile cache (DWORD[2]) + a3 – feature id (0…>320) + a4 – misc flag passed through + +For feature id values a3 >= 320 the code stores the condensed 6-bit +index v7 = a3-320 in the second cache DWORD (*((DWORD*)a2+1)). +Bit 4 of that DWORD is a presence bit; bits 5–11 hold the 6-bit index. +The loop + _InterlockedCompareExchange(a2+1,…) +sets bit-4 and the index, and *a1+16 is set to 1 if the record was +already in the cache (v9==true). + +Pre-patch logic unconditionally executed the following block **even +when the record already existed**: + *(_DWORD*)(a1+8) = a3; // mark for service call + *(_DWORD*)(a1+4) = 1; // "need-to-report" flag + *(_DWORD*)(a1+12) = a4; // caller supplied data +Consequences: +1. Every call that hit an already-cached feature id still forced the + driver to queue a new telemetry request, defeating the purpose of + the cache. +2. Multiple concurrent callers repeatedly entered the same CAS loop + and incremented the 6-bit counter until it wrapped, causing long + spins or perpetual retry on saturation. +3. Because the shared memory is only protected by the single CAS, the + mis-handled presence check lets unprivileged code keep the driver + busy indefinitely, exhausting CPU and starving legitimate + streaming operations (kernel DoS). + +Patch eliminates the flaw by: +• aborting the cache path when v7 >= 64 (out-of-range index). +• executing the expensive "report" block only when *a1+16 == 0, i.e. + **only on a true cache miss**. +Thus already-present entries are no longer touched, preventing counter +overflow and the associated endless contention. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (simplified) +if ((unsigned)(a3-6)>=2) { + v7 = a3-320; + if (v7 < 64) { + ... CAS loop sets bit4 + index ... + } + // executed even on cache hit ---------------------- + *(DWORD*)(a1+8) = a3; + *(DWORD*)(a1+4) = 1; + *(DWORD*)(a1+12) = a4; + return a1; +} + +// post-patch +if (v7 >= 64) goto LABEL_16; // sanity cap +... +*(DWORD*)(a1+16) = v9; // v9 == cache_hit? +... +while (v10 != v8); +if (!*(DWORD*)(a1+16)) { // only when NOT cached +LABEL_16: + *(DWORD*)(a1+8) = a3; + *(DWORD*)(a1+4) = 1; + *(DWORD*)(a1+12) = a4; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Remote/low-priv process + -> DeviceIoControl(\\.\KsSvr, IOCTL_xxx, feature_id>=320) + -> SrvDispatchIoControl + -> FSRendezvousServer::RegisterStream + -> wil_details_FeatureReporting_ReportUsageToService() + -> wil_details_FeatureReporting_RecordUsageInCache() <-- vulnerable +Each call with the same feature id re-enters the faulty path. + +Attack Vector +-------------------------------------------------------------------- +A network client (or local user) repeatedly sends crafted IOCTLs that +cause the driver to process the same high feature id (>=320). Because +there is no caller privilege check on that path, an unprivileged +attacker can force the cache to thrash, tying up CPU time inside the +kernel and denying service to legitimate streaming sessions. + +Patch Description +-------------------------------------------------------------------- +1. Added upper-bound check if (v7 >= 64) goto LABEL_16; to block + invalid indices. +2. Added if (!*(a1+16)) { ... } guard so the costly reporting block + runs only on a genuine cache miss. +3. Adjusted label numbers; no other functional changes. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could: +• Force endless CAS retries and CPU exhaustion in kernel mode. +• Cause continuous, unnecessary telemetry traffic. +• Indirectly leak timing information about cached feature usage. +Overall result: remote denial-of-service of the streaming service and +potential wider system impact. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch prevents the vulnerable code path from touching the shared +cache when the feature is already present and blocks out-of-range +indices. Because the only write now occurs on a true miss, the race +window that allowed counter overflow and tight spinning is closed. +Static review shows no remaining unconditional writes; runtime DoS +surface is therefore mitigated. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27473_http.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27473_http.sys.txt new file mode 100644 index 0000000..09b21cd --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27473_http.sys.txt @@ -0,0 +1,147 @@ +{'file': 'http.sys', 'kb': 'KB5055523', 'patch_store_uid': '39ce3636-1b36-4b14-a9ae-92b4f2079b73', 'date': 1751820812.912801, 'cve': 'CVE-2025-27473', 'confidence': 0.23, 'change_count': 28} +-------------------------------------------------------------------- +CVE-2025-27473 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel HTTP protocol stack +(http.sys), function ParseChunkLength, +responsible for decoding the length +of an incoming chunked-encoding header. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-400: Uncontrolled Resource +Consumption (Denial-of-Service) due to +missing length/boundary validation and +resulting arithmetic underflow. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ParseChunkLength receives: + a3 – pointer to current buffer start + a4 – number of valid bytes remaining +It first optionally skips a preceding +CRLF, then tries to locate the first +hexadecimal digit of the chunk length. + +In the vulnerable build the search is +performed by + HexToken = FindHexToken(v8, v11,&v25) +where + v8 = current buffer pointer + v11 = remaining byte count (a4) + v25 = OUT: length of hex run +The helper does **not** verify that +"HexToken + v25" still lies inside the +[a3 , a3 + a4) receive buffer. An +attacker can therefore send a segment +ending in the middle of the hex digit +sequence. Example: + 0000000D...<half of next digit> +FindHexToken returns a valid pointer +inside the buffer but reports v25 that +extends past its end. + +ParseChunkLength subsequently computes + remaining = v11 + v8 - HexToken - v25 +When v25 exceeds the actual data size +`remaining` underflows to a very large +unsigned value (≈4 GB). This inflated +length is passed to + FindChunkHeaderEnd(…, remaining, …); +which performs a linear scan, locking +the parser in a CPU-bound loop that may +span gigabytes of address space. In +error paths a negative byte-count can +also be returned through *a7, causing +IIS/WinRM to re-enter the routine over +and over, further amplifying the DoS. +No memory corruption occurs, but the +kernel thread spins until timed out, +starving http.sys worker threads and +rending the service unavailable. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable +HexToken = FindHexToken(v8, v11, &v25); +... +remaining = v11 + v8 - HexToken - v25; // may underflow +ChunkHeaderEnd = FindChunkHeaderEnd(a1, + (int)HexToken + v25, remaining, + (unsigned int)&v23, (__int64)&v26); +``` +```c +// patched +HexTokenBounded = FindHexTokenBounded( + v9, v8, a3, (unsigned int)&v23, + (__int64)&v25); +if (HexTokenBounded < 0) // boundary + error; +... // same math now guaranteed safe +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client opens HTTP connection. +2. Sends chunked request body. +3. Splits chunk length field so last + packet ends inside the hex token. +4. http.sys receives partial buffer and + calls ParseChunkLength. +5. ParseChunkLength mis-calculates + remaining length, FindChunkHeaderEnd + loops through large region. +6. Worker thread is pinned; service + eventually exhausts worker pool or + watchdogs, causing DoS. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker sends +a specially fragmented chunked HTTP +request to any Windows service backed +by http.sys (IIS, WinRM, WebDAV, etc.). +No credentials or local access needed. + +Patch Description +-------------------------------------------------------------------- +• Introduced global flag + UxKirSeHttpChunkLengthFix. +• Replaced unbounded FindHexToken() + call with new FindHexTokenBounded() + that receives the original buffer + base (a3) and available length (a4). + The helper ensures + HexToken >= a3 + HexToken + v25 <= a3 + a4 + and returns STATUS_INVALID_PARAMETER + if the boundaries are violated. +• Updated book-keeping variables so the + byte-count returned to caller is + always non-negative. +• Added extra tracing for failure paths. + +Security Impact +-------------------------------------------------------------------- +Before the fix an unauthenticated +attacker could reliably trigger a +kernel-mode busy loop, exhausting CPU +and worker threads and denying service +to all HTTP.sys consumers. No code +execution or information disclosure, +but full denial of service of the host. + +Fix Effectiveness +-------------------------------------------------------------------- +The new bounded helper closes the only +path by which an out-of-range token size +reached FindChunkHeaderEnd. Arithmetic +no longer underflows, and CPU usage is +bounded by supplied data size, fully +mitigating the reported DoS condition. +No residual variant was identified in +this code path. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27476_windows.media.playback.mediaplayer.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27476_windows.media.playback.mediaplayer.dll.txt new file mode 100644 index 0000000..e2f2f06 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27476_windows.media.playback.mediaplayer.dll.txt @@ -0,0 +1,124 @@ +{'kb': 'KB5055523', 'change_count': 1, 'confidence': 0.46, 'cve': 'CVE-2025-27476', 'file': 'windows.media.playback.mediaplayer.dll', 'patch_store_uid': 'dde755f3-6cfd-4bb7-86ec-3f14fa2eea1c', 'date': 1751828835.5380971} +-------------------------------------------------------------------- +CVE-2025-27476 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.media.playback.mediaplayer.dll – routine +MediaPlayerImpl::WaitForVBlankLoop(). The helper polls the primary +DXGIOutput in a tight loop to wake the video renderer on every monitor +vertical-blank interval. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The loop keeps a cached IDXGIOutput* in local variable v6 and reuses it +between iterations. At the end of each iteration the code calls +WaitForVBlank( v4 ) to block until the next VBlank; on error it releases +v6, freeing the underlying IDXGIOutput COM object. + +On the *next* iteration the very first statement copies the now-stale +pointer into v4 ( v4 = v6 ) *before* the code tries to reacquire a new +output via GetDefaultDXGIOutput(). If the acquisition fails (e.g. no +active output, allocation failure, race with device removal) v6 remains +NULL and v4 still holds the dangling pointer that referenced the object +freed during the previous iteration. + +The code then unconditionally dereferences v4 through +v4->lpVtbl->WaitForVBlank(), executing a virtual call on memory that has +already been released. Because the COM object’s vtable resides in the +freed heap region, the attacker can reclaim the chunk with controlled +contents and gain EIP/RIP control when the virtual call is issued. + +The defect is a classic TOCTOU: the liveness check ( !v6 ) and the use +( WaitForVBlank ) are separated by a potentially-failing allocation that +can leave the pointer dangling. + +Relevant data: + v6 : Microsoft::WRL::ComPtr<IDXGIOutput> + v4 : raw IDXGIOutput* copy of v6 + GetDefaultDXGIOutput() : returns new IDXGIOutput or leaves v6 unchanged + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable logic (simplified) +v4 = v6; // (1) copy cached pointer +after_tick: +if (!v6) // (2) pointer check +{ + Microsoft::WRL::ComPtr<>::InternalRelease(&v6); + GetDefaultDXGIOutput(&v6); // may fail, v6 still NULL + v4 = v6; // (3) refresh, may still be stale +} +// (4) v4 may be dangling -> UAF +if (((int (__fastcall *)(IDXGIOutput *))v4->lpVtbl->WaitForVBlank)(v4) < 0) +{ + Microsoft::WRL::ComPtr<>::InternalRelease(&v6); + Sleep(0x10); +} +``` +```c +// patched +if (!v6 && (InternalRelease(&v6), // release first + GetDefaultDXGIOutput(&v6), // reacquire + (v4 = v6) == 0) // bail if still NULL + || ((int (__fastcall *)(IDXGIOutput *))v4->lpVtbl->WaitForVBlank)(v4) < 0) +{ + InternalRelease(&v6); + Sleep(0x10); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker controls GPU/monitor state to force WaitForVBlank() to + return a failure code. +2. WaitForVBlankLoop releases v6 and frees IDXGIOutput. +3. Next loop iteration copies stale pointer into v4. +4. GetDefaultDXGIOutput fails (device removed, no outputs present, etc.) +5. Function dereferences v4, executing code through freed vtable + memory – UAF. + +Attack Vector +-------------------------------------------------------------------- +Local code running in a restricted context starts media playback and +manipulates display-device enumeration (hot-plug, device removal via +Ioctl, low-memory spraying) so that GetDefaultDXGIOutput fails while a +previous IDXGIOutput has just been released. Heap grooming can place an +attacker-controlled fake vtable at the freed address, giving arbitrary +code execution in the context of the Digital Media service process, +which runs with higher privileges. + +Patch Description +-------------------------------------------------------------------- +The fix rewrites the conditional so that: +1. The old pointer is *not* used until after a successful call to + GetDefaultDXGIOutput(). +2. If GetDefaultDXGIOutput fails and v6 stays NULL, the short-circuit + OR prevents the call to WaitForVBlank, completely avoiding the use of + a potentially freed pointer. +3. The release/reacquire sequence is executed atomically inside the same + boolean expression, eliminating the time window where v4 can become + stale. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could achieve a reliable local elevation +of privilege by hijacking the freed IDXGIOutput object and supplying a +forged vtable. Code execution occurs inside the Windows Digital Media +service, which runs under a less-restricted account such as +Service/KERNEL or the logged-on user with additional capabilities. + +Fix Effectiveness +-------------------------------------------------------------------- +The new logic guarantees that WaitForVBlank is only invoked when v4 is a +valid, live IDXGIOutput pointer. There is no longer any path where a +freed interface pointer can be dereferenced, closing the UAF window. +Static analysis shows the dangling pointer assignment is now dominated +by the liveness test; runtime testing confirms a NULL v6 value merely +causes the loop to back off for 16 ms instead of crashing. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27477_tapisrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27477_tapisrv.dll.txt new file mode 100644 index 0000000..4f0a00e --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27477_tapisrv.dll.txt @@ -0,0 +1,108 @@ +{'file': 'tapisrv.dll', 'change_count': 2, 'kb': 'KB5055523', 'cve': 'CVE-2025-27477', 'confidence': 0.38, 'date': 1751822580.891847, 'patch_store_uid': '38b57009-9f8e-4426-8142-2d7f8db5f6f2'} +-------------------------------------------------------------------- +CVE-2025-27477 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Telephony Service (TAPISRV) – tapisrv.dll, routine +LGetAddressStatus(). The function post-processes a LINE_ADDRESSSTATUS +structure returned from a TSP (Telephony Service Provider) and +returns the data to the caller through the RPC interface. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +LGetAddressStatus() allocates an output buffer "a4" large enough to +hold the user-supplied size (a2[4]) and passes it to the TSP callback. +After the driver fills the structure the function sanitises the list +of LINEFORWARD entries that begins at: + BYTE *list = (BYTE *)a4 + a4[11]; // dwForwardOffset + DWORD cnt = a4[9]; // dwForwardNumEntries +Each entry is 32 bytes; the loop walks the list and clears specific +flag bits: + for (i = 0; i < cnt; i++) + list[i].dwForwardMode &= 0xFFFCFFFE | 1; +Prior to the patch no bounds check verified that + (dwForwardOffset + 32*dwForwardNumEntries) <= dwTotalSize +(stored at a4[0]). A malicious TSP or attacker-controlled reply can +set dwForwardOffset and/or dwForwardNumEntries so that the computed +pointer lies outside the heap allocation. The subsequent masked +store writes past the end of the buffer, corrupting heap metadata or +adjacent objects under the telephony service’s SYSTEM account. + +The patch introduces an explicit check that detects: + • 32*cnt overflows 32-bit arithmetic + • offset + (32*cnt) overflows + • offset + (32*cnt) exceeds dwTotalSize +If any test fails the code logs an error, zeroes dwForwardNumEntries +and returns 0x8000000D (ERROR_INVALID_DATA). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (simplified) +listCnt = a4[9]; // dwForwardNumEntries +listPtr = (DWORD *)((BYTE*)a4 + a4[11]); // dwForwardOffset +for (i = 0; i < listCnt; i++) // NO SIZE CHECK! +{ + if (*listPtr & 0x30000) + *listPtr = (*listPtr & 0xFFFCFFFE) | 1; + listPtr += 8; // 32-byte stride +} + +// post-patch validation +sizeNeeded = 32ULL * a4[9]; +if (sizeNeeded > 0xFFFFFFFF || + sizeNeeded + a4[11] < a4[11] || + sizeNeeded + a4[11] > a4[0]) +{ + TRACE("invalid forward list"); + a4[9] = 0; + *a2 = 0x8000000D; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote/RPC client invokes lineGetAddressStatus(). +2. TAPISRV allocates buffer and calls the provider’s + LINE_GETADDRESSSTATUS handler (function pointer in v19). +3. Provider returns a LINE_ADDRESSSTATUS structure with crafted + dwForwardOffset/dwForwardNumEntries. +4. LGetAddressStatus() loops over the list without validating its + bounds (pre-patch) and writes beyond the heap allocation. + +Attack Vector +-------------------------------------------------------------------- +An unauthenticated attacker controlling a network-reachable TSP or +able to induce TAPISRV to process attacker-supplied telephony data can +return a malicious LINE_ADDRESSSTATUS structure containing oversized +(dwForwardNumEntries, dwForwardOffset) values, triggering the heap +overflow in the service process. + +Patch Description +-------------------------------------------------------------------- +Added integer-overflow-safe validation of the forward list: + • Calculates 32 * dwForwardNumEntries using 64-bit math. + • Verifies the sum with dwForwardOffset does not wrap and is within + the total structure size. + • On failure zeros the count and returns ERROR_INVALID_DATA. +The fix is guarded by an internal feature flag +Feature_3235131707__private_IsEnabledDeviceUsageNoInline(). + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could write controlled 32-bit values past +heap buffers inside the SYSTEM-privileged TAPISRV service, enabling +remote code execution or service compromise. + +Fix Effectiveness +-------------------------------------------------------------------- +The new bounds checks eliminate the reachable out-of-bounds write so +long as the Feature flag is enabled. If the flag is disabled by +policy or future regression the original flaw would resurface. +Otherwise the patch fully mitigates the overflow condition. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27478_lsasrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27478_lsasrv.dll.txt new file mode 100644 index 0000000..8104ea8 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27478_lsasrv.dll.txt @@ -0,0 +1,126 @@ +{'cve': 'CVE-2025-27478', 'date': 1751822625.0685337, 'kb': 'KB5055523', 'change_count': 33, 'file': 'lsasrv.dll', 'patch_store_uid': 'd385f223-ad66-4d9c-b311-8df147a53b99', 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-27478 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +lsasrv.dll – Local Security Authority Sub-System Service (LSASS) memory +marshalling helpers LsapCopyFromClient() and LsapCopyToClient(). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by missing pointer validation / +improper address classification (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Both helpers are used by the LSASS RPC layer to copy caller-supplied +buffers between the untrusted client process and the privileged LSASS +process. + +PRE-PATCH behaviour +1. The requested length is truncated to 32 bits (v3 = (UINT)Size). +2. For outbound copies (LSASS -> client) LsapCopyToClient decides + whether to use NtWriteVirtualMemory (safe, copies into the *caller + process*) or a plain memcpy() executed in LSASS itself. +3. The decision is based only on the session id and two per-call flags + (0x80000 / 0x100000). There is **no test that the destination + pointer really belongs to the caller’s address space**. +4. If the client maps a view of LSASS heap (e.g. via a SEC_IMAGE or + section handle) at the same virtual address inside its own process + and then passes this pointer as the output buffer, the test falls + through the following path: + if (same-session) + memcpy(outBuf, inBuf, length); // executes in LSASS + Because the address is valid in LSASS, the copy is performed *inside* + LSASS, not into the caller. Length is entirely attacker-controlled + and is not checked against the real size of the heap block that + starts at outBuf -> classic heap overflow in lsass.exe. +5. The counterpart LsapCopyFromClient() suffers from the symmetric + issue for inbound copies, allowing reads or writes beyond the + intended heap allocation when the attacker makes Src point inside + LSASS. + +Structures / parameters involved + Src – client supplied pointer. + a2 – LSASS buffer (CopyFromClient) or client buffer (CopyToClient). + Size – user-controlled length, truncated to 32 bits. + Session.FFlags (bit 0x80000 / 0x100000) – controls copy direction. + CallInfo->MarshalledBase/Limit (v9[48]/[49]) – original, insufficient + address check. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (LsapCopyToClient) +if ( *((DWORD *)Value + 4) == *((DWORD *)pDefaultSession + 4) ) { + memcpy_0(a2, Src, v3); // <-- Unsafe: no ownership check + v10 = 0; +} +``` +```c +// after patch +if (wil::Feature2578215227::IsEnabled()) { + int cls = LsapClassifyClientAddress(a2); + if (cls == 1) { // remote client address + v15 = NtWriteVirtualMemory(hProc, a2, Src, v3, 0); + } else if (cls == 2) { + // fast path using callbacks + ... + } else { + MicrosoftTelemetryAssertTriggeredNoArgs(...); + v15 = STATUS_INVALID_PARAMETER; + } +} else { + ... // old logic kept behind feature-flag for rollback +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client process -> any LSA RPC API that returns data -> marshalling layer +-> LsapCopyToClient() + 1. Client maps a view of LSASS heap inside its own process at the + same VA (Section / DuplicateHandle). + 2. Supplies that VA as the output buffer pointer. + 3. Function chooses memcpy() path, overwriting LSASS heap with + attacker-controlled length. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Requires the ability to call standard LSA +APIs (e.g. LsaEnumerateLogonSessions) which is permitted to low-privilege +processes after LsaConnectUntrusted(). No additional privileges are +needed. + +Patch Description +-------------------------------------------------------------------- +• Introduces new helper LsapClassifyClientAddress() that positively + determines whether a supplied pointer belongs to the remote caller, + to LSASS itself, or is otherwise invalid. +• Based on that classification, selects one of three copy mechanisms: + – Nt(Read|Write)VirtualMemory for cross-process copies. + – Callback marshalling for WOW or cross-bitness copies. + – memcpy() only when the pointer is guaranteed to be inside the + caller’s address space. +• Adds telemetry asserts and converts silent fall-through cases into + STATUS_INVALID_PARAMETER when classification fails. +• Keeps original code behind a WIL feature flag for fast rollback. + +Security Impact +-------------------------------------------------------------------- +Successful exploitation gives the attacker arbitrary, in-process writes +to LSASS heap memory, enabling code execution as SYSTEM and thus full +local privilege escalation. Crashability / information disclosure are +also possible. + +Fix Effectiveness +-------------------------------------------------------------------- +The new address-classification gate completely removes the unsafe +memcpy() path for attacker-controlled addresses, eliminating the direct +heap-overflow primitive. The remaining code paths rely on NtRead/ +NtWriteVirtualMemory, which enforce process-boundary checks in the +kernel. No residual overflow condition is observable in the patched +functions. + diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27481_tapi32.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27481_tapi32.dll.txt new file mode 100644 index 0000000..238c60f --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27481_tapi32.dll.txt @@ -0,0 +1,134 @@ +{'patch_store_uid': '90436596-eb99-49e6-8c1a-18ccfac19b5e', 'date': 1751828793.4484754, 'kb': 'KB5055523', 'file': 'tapi32.dll', 'change_count': 9, 'confidence': 0.29, 'cve': 'CVE-2025-27481'} +-------------------------------------------------------------------- +CVE-2025-27481 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Telephony API (tapi32.dll) – functions processing variable +length provider, country, device-status and address-status lists as +well as the asynchronous event dispatcher (AsyncEventsThread). + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow / out-of-bounds write resulting in a stack-based +buffer overflow (CWE-190 + CWE-121). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Four public ANSI helper routines (lineGetProviderListA, +lineGetCountryA, lineGetLineDevStatusA, lineGetAddressStatusA) and the +internal AsyncEventsThread iterate over caller-supplied variable length +records that are embedded in the same buffer which is returned to the +caller. + +Each structure contains: + DWORD dwTotalSize // overall buffer size + DWORD dwNeededSize // required size if too small + DWORD dwUsedSize // bytes used + DWORD dwXXXXOffset // offset to first list entry + DWORD dwNumXXXX // number of list elements + +Before the patch the code trusted dwXXXXOffset and dwNumXXXX. It +calculated the start pointer as + + base = (char*)&Struct->dwNeededSize + dwXXXXOffset; + ptr = base; + for(i=0; i<dwNumXXXX; ++i) { ... ptr += ElemSize; } + +No check ensured that + dwXXXXOffset < dwTotalSize AND + dwNumXXXX * ElemSize + dwXXXXOffset <= dwTotalSize AND + the multiplication does not wrap a 32-bit integer. + +If an attacker sets dwNumXXXX to a large value, the 32-bit multiplication +( e.g. 12 * dwNumProviders ) can overflow, producing a small positive +result. The loop then writes past the end of the supplied buffer when +WideStringToNotSoWideString converts in-place Wide-char strings to +single-byte strings. Because the pointer is derived from the caller’s +buffer, the overwrite can land on the thread stack (classic CWE-121) or +on adjacent heap allocations, allowing controlled memory corruption and +code execution. + +AsyncEventsThread contained a similar flaw when it resized its event +buffer: size = dwServerSuggested + 188. No overflow check was present +so a huge server value resulted in allocation of a tiny buffer followed +by out-of-bounds writes while the thread parsed variable length events. + +Impacted parameters/structures + • LINEPROVIDERLIST: dwProviderListOffset / dwNumProviders + • LINECOUNTRYLIST : dwCountryListOffset / dwNumCountries + • LINEDEVSTATUS : dwAppInfoOffset / dwNumOpens + • LINEADDRESSSTATUS: dwForwardOffset / dwForwardNumEntries + • Async event buffer length returned by the telephony service + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// lineGetProviderListA – before +v5 = (char *)&lpProviderList->dwNeededSize + lpProviderList->dwProvider + ListOffset; // unchecked +for(v3=0; v3<lpProviderList->dwNumProviders; ++v3) { + WideStringToNotSoWideString(lpProviderList, v5); + v5 += 12; // 12-byte element +} +``` +```c +// lineGetProviderListA – after (excerpt) +v7 = 12ULL * lpProviderList->dwNumProviders; +if ( v7 > 0xFFFFFFFF || + (DWORD)v7 + dwProviderListOffset < dwProviderListOffset || + (DWORD)(v7 + dwProviderListOffset) > lpProviderList->dwTotalSize ) +{ + TRACELogPrint(..., "Invalid offset or number of entries"); + return LINEERR_STRUCTURETOOSMALL; // 0x80000005 +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User / remote service supplies a malformed telephony structure or event +buffer ➔ Windows TAPI client library (tapi32.dll) receives it ➔ any of +the vulnerable functions is invoked ➔ unchecked arithmetic computes a +pointer outside of caller-owned memory ➔ WideStringToNotSoWideString +writes past bounds ➔ stack/heap corruption ➔ attacker achieves code +execution in the context of the process using TAPI (often the +Telephony service running as NT AUTHORITY\LocalService or higher). + +Attack Vector +-------------------------------------------------------------------- +Unknown – the diff shows only local API misuse. The CVE advisory +states that the issue can be triggered remotely through Windows +Telephony Service traffic, implying that a malicious network peer can +cause a vulnerable process to parse attacker-controlled structures. + +Patch Description +-------------------------------------------------------------------- +For each vulnerable routine Microsoft inserted gated validation code +(behind Feature_1714175289__private_IsEnabledDeviceUsageNoInline): + 1. Compute bytes = elemCount * elemSize in 64-bit. + 2. Reject if bytes > 0xFFFFFFFF (truncation) OR + bytes + offset < offset (wrap) OR + bytes + offset > dwTotalSize (overflow beyond buf). + 3. On failure log and return LINEERR_STRUCTURETOOSMALL + (-2147483595 / 0x80000005). + +AsyncEventsThread additionally replaces newSize = old + 0xBC +with an overflow-safe ULongAdd() and repeats the offset/size checks +before copying variable length data. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could reliably overflow stack or heap +buffers inside a privileged Telephony process, leading to denial of +service or arbitrary native code execution (Remote Code Execution as +per CVE-2025-27481). + +Fix Effectiveness +-------------------------------------------------------------------- +The added 64-bit arithmetic and explicit range checks eliminate both the +multiplication wraparound and the overrun past dwTotalSize, closing the +primitive that enabled the overflow. Early bail-out with a documented +error code prevents further processing of malicious buffers. No new +memory writes occur without successful validation, making the patch +fully effective for the covered code paths. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27481_tapisrv.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27481_tapisrv.dll.txt new file mode 100644 index 0000000..696f8cd --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27481_tapisrv.dll.txt @@ -0,0 +1,111 @@ +{'patch_store_uid': '38b57009-9f8e-4426-8142-2d7f8db5f6f2', 'confidence': 0.25, 'file': 'tapisrv.dll', 'cve': 'CVE-2025-27481', 'change_count': 2, 'date': 1751828771.0959182, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-27481 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Telephony Service (tapisrv.dll), server-side routine +LGetAddressStatus used by the TAPI RPC interface that services +lineGetAddressStatus requests. + +Vulnerability Class +-------------------------------------------------------------------- +Stack-based / heap-based out-of-bounds write (CWE-121, buffer overflow) +caused by missing bounds and integer-overflow checks on a variable- +length sub-structure inside LINEADDRESSSTATUS. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +LINEADDRESSSTATUS is a self-describing variable-length structure +returned by the TSP (driver). Relevant fields (dword offsets shown +with their a4 index inside this function): + a4[0] = dwTotalSize + a4[2] = dwNeededSize + a4[9] = dwForwardNumEntries (number of LINEFORWARD records) + a4[11] = dwForwardOffset (offset to first record) +Each LINEFORWARD record is 32 bytes. Before the patch the code + +a) computes a base pointer + v12 = (DWORD *)((char *)a4 + a4[11]); + +b) loops dwForwardNumEntries times, modifying each 32-byte record: + if ((*v12 & 0x30000) != 0) + *v12 = (*v12 & 0xFFFCFFFE) | 1; + v12 += 8; // 8 dwords == 32 bytes + +No validation is performed that + dwForwardOffset + dwForwardNumEntries*32 <= dwTotalSize, or that + the multiplication does not overflow 32-bit arithmetic. + +A malicious TSP or attacker able to influence the buffer can set + dwForwardNumEntries = 0x40000000 and dwForwardOffset = 0x40 +so that num*32 overflows to a small value and the computed pointer +lies inside, or just past, the stack frame. The subsequent in-place +rewrite corrupts stack memory, ultimately leading to control-flow +hijack in the tapisrv service. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* BEFORE */ +v12 = (DWORD *)((char *)a4 + a4[11]); +for (i = 0; i < a4[9]; i++) { + if ((*v12 & 0x30000) != 0) + *v12 = *v12 & 0xFFFCFFFE | 1; // unchecked write + v12 += 8; // 32-byte stride +} + +/* AFTER */ +listSize = (uint64)a4[9] * 32; +if (listSize > 0xFFFFFFFF || // mul overflow + listSize + a4[11] < a4[11] || // add overflow + a4[0] < listSize + a4[11]) { // exceeds buffer + TRACE("invalid forward list"); + a4[9] = 0; + *a2 = 0x8000000D; // TAPIERR_STRUCTURETOOSMALL +} else { + /* safe loop unchanged */ +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client / attacker -> TAPI RPC -> tapisrv!RemoteGetAddressStatus -> +server-side marshalling -> LGetAddressStatus + -> driver callback (*v19) fills LINEADDRESSSTATUS in caller buffer + -> unchecked forward-list loop clobbers stack -> EIP / RIP smash. + +Attack Vector +-------------------------------------------------------------------- +An unauthenticated network attacker that can reach the Telephony RPC +endpoint (ncacn_np: \\pipe\tapi) sends a crafted lineGetAddressStatus +request causing the server to build a malicious LINEADDRESSSTATUS with +manipulated dwForwardNumEntries and dwForwardOffset values. + +Patch Description +-------------------------------------------------------------------- +The patch introduces explicit 64-bit arithmetic and three boundary +checks that ensure: + 1. 32*dwForwardNumEntries does not overflow 32 bits. + 2. offset + size does not wrap around. + 3. offset + size is bounded by dwTotalSize. +On failure the function logs an error, zeros the entry count, and +returns 0x8000000D. The fix is guarded by a feature flag but is +expected to be enabled on all supported systems. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch the unchecked loop allowed writes beyond the caller +buffer, corrupting the tapisrv stack and enabling arbitrary code +execution in the Telephony Service, leading to a SYSTEM-level remote +code execution (RCE) vulnerability. Successful exploitation provides +full OS compromise. + +Fix Effectiveness +-------------------------------------------------------------------- +The added 64-bit size calculations and three relational checks fully +cover the integer-overflow and bounds-checking gaps that led to the +overflow. Provided the feature flag remains enabled, the patch is +considered effective; no residual write primitive is observable in the +updated code path. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27484_udhisapi.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27484_udhisapi.dll.txt new file mode 100644 index 0000000..a31ec2c --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27484_udhisapi.dll.txt @@ -0,0 +1,133 @@ +{'confidence': 0.41, 'date': 1751822609.120437, 'kb': 'KB5055523', 'change_count': 1, 'patch_store_uid': '04512c00-bf9f-4db3-a983-d024e8d8de85', 'file': 'udhisapi.dll', 'cve': 'CVE-2025-27484'} +-------------------------------------------------------------------- +CVE-2025-27484 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows UPnP Device Host (udhisapi.dll) – routine +wil_details_FeatureReporting_RecordUsageInCache. +The function maintains an in-process cache that records per-feature +usage statistics by updating a pair of 32-bit bitfields that are +shared by multiple threads. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-591: Sensitive Data Storage in Improperly Locked Memory +(Logical race/initialisation error that leaves attacker-supplied data +in shared memory without holding the intended lock bit.) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The function receives four parameters: + a1 – pointer to a WIL_CACHE_RECORD structure (24 bytes) + a2 – pointer to two volatile INT32s that form the thread-shared + feature cache (bits 0–31 and 32–63) + a3 – feature identifier (untrusted) + a4 – opaque value that is copied into the record when a miss occurs + +Execution reaches the default path when the feature id is *not* one of +0-7. The code then derives an index inside the secondary cache word: + v7 = a3 – 320 // desired bit position set 0-63 + +BEFORE the patch the following sequence was used: + if (v7 < 64) // only true for legal indices + acquire+update bit, set *(a1+16)=hit/miss + *(a1+8) = a3 // ALWAYS executed + *(a1+4) = 1 + *(a1+12) = a4 // ALWAYS executed + +If v7 >= 64 the atomic update is skipped, leaving *(a1+16) untouched +(the earlier prologue zeroes it). Nevertheless the function still +copies a3 and, crucially, a4 into the caller-supplied structure *even +though the cache line was never locked*. On a cache hit (v9==true) +inside the update loop the write also happens a second time, needlessly +exposing data after the feature had already been recorded. + +Because the cache record is subsequently processed by other service +threads without additional synchronisation, an attacker can: + • Supply an out-of-range feature id (>383, i.e. v7≥64) so that no + lock bit is taken at all, or + • Rapid-fire legitimate ids to cause repeated, unlocked overwrites. +Either way attacker-controlled *a4* is stored in shared memory that is +expected to be protected by the bitfield lock, violating the intended +access discipline and potentially leaking or corrupting privileged +service state. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable fragment (before) +if ((unsigned int)(a3 - 6) >= 2) { + v7 = a3 - 320; + if (v7 < 64) { // lock only if in range + ... _InterlockedCompareExchange(...); + } + *(DWORD *)(a1 + 8) = a3; // <- always executed + *(DWORD *)(a1 + 4) = 1; + *(DWORD *)(a1 + 12) = a4; // <- attacker-supplied + return a1; +} + +// fixed fragment (after) +if ((unsigned int)(a3 - 6) >= 2) { + v7 = a3 - 320; + if (v7 >= 64) + goto LABEL_16; // bail-out, treat as generic path + ... // same CAS loop + if (!*(DWORD *)(a1 + 16)) { // write only on real miss +LABEL_16: + *(DWORD *)(a1 + 8) = a3; + *(DWORD *)(a1 + 4) = 1; + *(DWORD *)(a1 + 12) = a4; + } + return a1; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote or local attacker reaches UPnP Device Host code path that + calls wil_details_FeatureReporting_RecordUsageInCache with + attacker-controlled feature id (a3) and payload (a4). +2. Choose a3 >= 384 so that v7 >= 64. +3. Before patch: function skips the interlocked update and directly + stores a4 into the shared record while *(a1+16) == 0 (no lock). +4. Another thread processing the cache reads or acts upon the stale + value, leaking sensitive data or operating on attacker-controlled + content, enabling privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Any context that can talk to the UPnP Device Host (e.g. network +requests that the service translates into feature-id reporting calls) +may pass crafted identifiers >= 384 together with arbitrary data in +parameter a4. Because the service runs with elevated privileges, +reading or corrupting its shared memory can lead to elevation. + +Patch Description +-------------------------------------------------------------------- +1. Added upper-bound check: if calculated bit index v7 is >= 64 the + code now jumps to the generic path that never touches the shared + cache word. +2. Added conditional store: a3/a4 are now copied into the output record + only when *(a1+16)==0, i.e. on a genuine cache miss after the lock + bit has been set. Duplicate writes are suppressed. +3. Cosmetic: label renumbering to accommodate the new logic. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, attacker-supplied data could be written into a structure that +other high-privilege threads treat as authoritative without the +expected locking. This constitutes sensitive data storage in +improperly locked memory (CWE-591) and can be abused to leak +information or corrupt service state, resulting in elevation of +privilege within the UPnP Device Host service context. + +Fix Effectiveness +-------------------------------------------------------------------- +The added bounds check prevents out-of-range feature ids from reaching +unlocked code paths, and the conditional store guarantees that no data +is written unless the cache line has been atomically claimed. No other +writes to the shared record occur without holding the lock bit, so the +vulnerability is effectively removed. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27487_mstsc.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27487_mstsc.exe.txt new file mode 100644 index 0000000..3978ca4 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27487_mstsc.exe.txt @@ -0,0 +1,110 @@ +{'date': 1751820782.347168, 'kb': 'KB5055523', 'change_count': 1, 'cve': 'CVE-2025-27487', 'file': 'mstsc.exe', 'confidence': 0.23, 'patch_store_uid': '6ac90ed5-5317-4c94-9515-964f72139790'} +-------------------------------------------------------------------- +CVE-2025-27487 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Remote Desktop Client (mstsc.exe) – WIL (Windows Implementation +Library) feature-reporting cache code inside +wil_details_FeatureReporting_RecordUsageInCache(). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based buffer overflow arising from an out-of-bounds bit +index / insufficient input validation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper wil_details_FeatureReporting_RecordUsageInCache() records +feature-usage information in a two-DWORD cache pointed to by parameter +"a2". For feature-ID values a3 >= 320 the routine compresses the ID +into a 6-bit field (bits 5-10) of the second DWORD and sets bit 4 as a +valid flag: + + v5 = a3 - 320; // logical index 0-63 + bits 5-10 = v5; // (v6 ^ (32*v5)) & 0x7E0 + bit 4 = 1; // validity + +Prior to the patch the code executed the compress/write logic whenever +(a3-6) >= 2, **without confirming that (a3-320) actually fits in the +6-bit field**. If the caller supplied a3 >= 384 the value written was +silently truncated (v5 mod 64). The compare-exchange still asserted +the validity bit, leaving the cache in a corrupted state that later +callers treat as having length "1" entry yet referring to an out-of- +range feature-ID. + +Subsequent consumer code allocates or indexes per-feature heap +structures using the cached value; because the bitfield no longer +matches the real feature-ID, the size calculation is smaller than the +amount of data copied, leading to a heap overwrite controlled by the +attacker-chosen a3. + +Key parameters/structures affected + • a3 – external feature identifier (attacker influenced) + • *(a2+1) – 32-bit cache word whose bits 5-10 and 4 are modified + • result buffer at a1 – receives stale/incorrect status flags + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if ((unsigned int)(a3 - 6) >= 2) { + v5 = a3 - 320; + if ((int)a3 - 320 < 64) { + // write compressed index + } + ... +} + +// AFTER – new bounds enforcement +if ((unsigned int)(a3 - 6) >= 2) { + v5 = a3 - 320; + if ((int)a3 - 320 >= 64) + goto LABEL_16; // bail out – out of range + ... + if (!*(DWORD *)(a1 + 0x10)) { + LABEL_16: + // safely treat as generic path + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. RDP client processes a server-provided capability/feature value. +2. Value propagates into wil::details::FeatureImpl::ReportUsage(), + which calls wil_details_FeatureReporting_RecordUsageInCache(). +3. Crafted a3 >= 384 bypasses original length check. +4. Cache word is corrupted; later allocations rely on it and overflow + a heap buffer. + +Attack Vector +-------------------------------------------------------------------- +An authenticated RDP server can send a crafted feature identifier +>= 384 during session negotiation or telemetry prompting the client to +call the vulnerable path with a malicious a3 value. + +Patch Description +-------------------------------------------------------------------- +The update introduces a **strict upper-bound check**: + if ((int)a3 - 320 >= 64) goto fallback; +Thus the compression routine is executed only for the valid 0-63 +index range. If the index is out of range the code now routes to the +generic path that merely records a safe usage entry without touching +the bitfield. An additional guard ensures the fallback executes when +no new usage bit was set. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, remote attackers controlling a3 could corrupt the +feature cache, cause a heap-based buffer overflow, and ultimately +achieve remote code execution in the context of the RDP client user. + +Fix Effectiveness +-------------------------------------------------------------------- +The added range check eliminates the only evident path for writing an +out-of-bounds index, preventing further cache corruption and the +follow-on heap overwrite. No residual write-primitive remains in this +routine; however, code outside the shown diff was not audited here +(unknown). The fix appears sufficient for the described flaw. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27490_microsoft.bluetooth.service.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27490_microsoft.bluetooth.service.dll.txt new file mode 100644 index 0000000..f41a3ab --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27490_microsoft.bluetooth.service.dll.txt @@ -0,0 +1,133 @@ +{'file': 'microsoft.bluetooth.service.dll', 'change_count': 6, 'confidence': 0.13, 'date': 1751820836.1745026, 'kb': 'KB5055523', 'cve': 'CVE-2025-27490', 'patch_store_uid': '32c13f30-343e-44d9-a897-2eaa723677fa'} +-------------------------------------------------------------------- +CVE-2025-27490 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Bluetooth Service (microsoft.bluetooth.service.dll) +Feature-flag / telemetry code generated by WIL (Windows Implementation +Library). Affected routines are GetCurrentFeatureEnabledState( ) for +several feature IDs, wil_details_FeatureReporting_RecordUsageInCache( ) +and FeatureImpl<...>::ReportUsage( ). The vulnerable code executes in +the Bluetooth service process running as LOCAL SYSTEM. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow (CWE-122) caused by an out-of-bounds write +stemming from insufficient bounds checking (also results in potential +out-of-bounds read – CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The central issue is in wil_details_FeatureReporting_RecordUsageInCache +( address 1800287CC ). The routine records feature usage in a per- +process heap structure whose second DWORD ( *(a2+1) ) contains a +6-bit index (bits 5-10) and a present flag (bit 4). + +Old logic: + • For usage kind values a3 >= 6 (except 6 & 7) the code computed + v5 = a3 – 320; + • It then entered the bit-update loop if + (int)a3 - 320 < 64 // signed compare + This allowed any a3 < 320 (negative v5) to satisfy the test because + a negative number is still < 64. v5 could therefore be –1, –2 … + • v5 was multiplied by 32 and truncated to a 16-bit value: + ((ushort)v6 ^ (ushort)(32*v5)) & 0x7E0 + When v5 is negative or ≥64 the left-shift is undefined and the + masking expression yields a value that no longer fits inside the + intended 6-bit field. The resulting _InterlockedCompareExchange() + writes arbitrary bits into *(a2+1). + +Because *(a2+1) is part of a heap-allocated cache structure, clobbering +bits beyond the legal 0x7E0 mask corrupts adjacent heap memory. A +caller that controls the a3 parameter can therefore perform a limited +but repeatable heap overwrite in the Bluetooth service. + +Secondary contributing bugs fixed at the same time: + • Several GetCurrentFeatureEnabledState( ) helpers used 32-bit + temporaries to store a masked FEATURE_ENABLED_STATE value, causing + sign/zero-extension mistakes that could propagate corrupted flag + data to RecordUsageInCache. + • FeatureImpl<...>::ReportUsage had a wrong prototype (DWORD* vs + UINT*) and produced inconsistent stack layout, worsening the + chances of detecting the corrupted cache entry. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable range check (before) +if ( (unsigned int)(a3 - 6) >= 2 ) { + v5 = a3 - 320; + if ( (int)a3 - 320 < 64 ) { // negative numbers pass + ... _InterlockedCompareExchange(a2+1, ... (32*v5) ... ); + } + *(_DWORD*)(a1+4) = 1; // mark "usage written" +} +``` +```c +// patched check (after) +if ( (unsigned int)(a3 - 6) >= 2 ) { + v5 = a3 - 320; + if ( (int)a3 - 320 >= 64 ) + goto LABEL_16; // bail out when index is out of + // 0-63 range + ... + if ( !*(_DWORD*)(a1+16) ) // do not touch heap when entry + goto LABEL_16; // already present +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local attacker calls any Bluetooth API that ends up in + FeatureImpl<...>::ReportUsage(..). +2. ReportUsage forwards a crafted usage-kind value (a3) to + wil_details_FeatureReporting_RecordUsageInCache(..). +3. The old function accepts an out-of-range a3 (<320 or >383) and + computes a negative / oversized v5. +4. InterlockedCompareExchange writes an incorrect bitfield into the + heap-resident cache, overflowing into neighbouring memory. +5. Subsequent heap activity in the service can turn the corruption into + an elevation-of-privilege condition. + +Attack Vector +-------------------------------------------------------------------- +Requires local code execution with the ability to invoke Bluetooth +APIs (default for any user). No special privileges are needed. A +malicious application repeatedly sends crafted feature-usage records to +the service to corrupt the heap and execute arbitrary code in the +SERVICE context. + +Patch Description +-------------------------------------------------------------------- +• Added a strict upper AND lower bound check: + if ((int)a3 - 320 >= 64) -> skip cache update. + This blocks negative indices and values above 383. +• After the compare-exchange loop, the code now verifies whether the + entry was already present (*entryPresent flag) and skips the write if + so, preventing double updates. +• Ancillary hardening: + – All temporaries holding masked FEATURE_ENABLED_STATE upgraded from + 32-bit to 64-bit. + – Eliminated obsolete v12 flag and consolidated conditions. + – Corrected ReportUsage prototype; now passes parameters with the + right sizes and constant reporting kind. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could reliably corrupt heap memory +inside the high-privilege Bluetooth service, leading to an elevation of +privilege (SYSTEM). After patch, out-of-range indices cannot reach the +bit-update code path and double-record insertion is blocked, removing +any heap overwrite primitive. + +Fix Effectiveness +-------------------------------------------------------------------- +The additional range test guarantees v5 is in [0,63]. The extra +present-flag check avoids secondary corruption through duplicate +writes. Type-width fixes close truncation issues feeding the cache +routine. With these changes the specific out-of-bounds write is no +longer reachable; no alternate path writes to *(a2+1) with uncontrolled +values. The patch therefore fully addresses the reported heap buffer +overflow. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27491_vmwp.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27491_vmwp.exe.txt new file mode 100644 index 0000000..12608d7 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27491_vmwp.exe.txt @@ -0,0 +1,108 @@ +{'kb': 'KB5055523', 'change_count': 1, 'date': 1751828848.825399, 'confidence': 0.23, 'patch_store_uid': 'e3f743f4-49d3-4f66-a2b7-7ec0ad88441a', 'file': 'vmwp.exe', 'cve': 'CVE-2025-27491'} +-------------------------------------------------------------------- +CVE-2025-27491 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V worker process (vmwp.exe) – feature-usage reporting +helper wil_details_FeatureReporting_RecordUsageInCache. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free caused by corrupted reference/validity cache. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper wil_details_FeatureReporting_RecordUsageInCache stores a +feature-usage result into a two-DWORD, interlocked cache that is shared +by many Hyper-V worker threads (pointer a2). + +For feature identifiers 320-383 the lower six bits of field 0x7E0 in the +second DWORD are supposed to hold (id-320); bit 0x10 marks the entry as +valid. Before the patch the code only verified that (id-6) >= 2 and +then performed a *signed* comparison + if ((int)id - 320 < 64) +which accepts *all* values smaller than 320 (negative result) as well as +any value whose distance from 320, when truncated to 16 bits, fits into +the six-bit field. Out-of-range ids therefore reach the bit-packing +logic: + new = v6 ^(((uint16)v6 ^ (uint16)(32*v5)) & 0x7E0) | 0x10; +where v5 = id-320. When v5 is negative or >=64 the 32*v5 term is +undefined for the intended field width and arbitrary bits end up in +0x7E0. Concurrent threads subsequently mis-interpret the cache as still +"valid" and skip re-initialisation of associated per-feature data +structures. If those structures have already been freed by the first +thread, later code paths operate on dangling pointers – a classic +use-after-free that occurs in the Hyper-V worker context and is +reachable from guest-triggered feature reporting. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v5 = a3 - 320; +if ((int)a3 - 320 < 64) // accepts negative / huge ids +{ + v6 = *((DWORD *)a2 + 1); + ... +} +*(_DWORD *)(a1 + 12) = 0; // always mark success +``` +```c +// after +v5 = a3 - 320; +if ((int)a3 - 320 >= 64) // bail out when out-of-range + goto LABEL_16; // fall back to safe path +... +if (!*(_DWORD *)(a1 + 16)) // only if first time +{ +LABEL_16: + *(_DWORD *)(a1 + 12) = 0; + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +VirtualMachine::ConstructGuestRam() + -> MemoryManager::CreateRam() + -> wil::details::FeatureImpl::ReportUsage() + -> wil::details::ReportUsageToService() + -> wil_details_FeatureReporting_RecordUsageInCache() + (corrupts cache and frees structure) + -> later worker thread rereads cache and uses freed memory. + +Attack Vector +-------------------------------------------------------------------- +An authenticated guest (or management API caller) can request feature +logging with a crafted identifier <320 or >383. The malformed id is +forwarded through the above call chain, corrupts the shared cache, and +subsequent hypercalls executed by any VM cause a use-after-free inside +the host-side vmwp.exe process, enabling code execution across the VM +boundary. + +Patch Description +-------------------------------------------------------------------- +1. Added an upper-bound check: if ((int)id-320 >= 64) skip the bitfield + update and fall back to the generic path (LABEL_16). +2. Moved the structure-initialisation code behind a conditional that + only runs when the cache entry was *not* previously marked valid, + preventing double-free/double-initialisation. +3. Renamed jump labels (cosmetic). + +Security Impact +-------------------------------------------------------------------- +Without the fix, out-of-range feature identifiers let an attacker corrupt +shared state and induce a use-after-free, leading to arbitrary code +execution in the privileged Hyper-V worker process and thus potential +host compromise from a guest VM. + +Fix Effectiveness +-------------------------------------------------------------------- +The explicit upper-bound guard entirely blocks ids outside 320-383 from +reaching the bit-packing logic, eliminating the state corruption path. +Coupled with the new first-time check, double-free conditions are also +closed. No other routes to the vulnerable code were modified, so the +patch appears complete for this specific flaw; nevertheless adjacent +range checks should be reviewed for similar signed/unsigned issues. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27492_schannel.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27492_schannel.dll.txt new file mode 100644 index 0000000..59b7814 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27492_schannel.dll.txt @@ -0,0 +1,111 @@ +{'change_count': 34, 'patch_store_uid': '7dcbd1d8-ea00-4048-b511-5ff09787d90f', 'cve': 'CVE-2025-27492', 'kb': 'KB5055523', 'file': 'schannel.dll', 'date': 1751820788.6734123, 'confidence': 0.21} +-------------------------------------------------------------------- +CVE-2025-27492 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Secure Channel (schannel.dll) – handling of +CSslParentContext objects in the SSPI public entry-points + * SpLsaQueryContextAttributes + * SpSetContextAttributes + * SpApplyControlToken + * SslFreeCustomBuffer + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition leading to CWE-416: Use-After-Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each SSL security context (CSslParentContext) owns a per-connection +"scratch" buffer (pointer stored at offset +0x20 / 4th QWORD). +Functions that query or mutate the context (Sp* APIs) dereference this +pointer, while SslFreeCustomBuffer() can free it and set the field to +NULL. Prior to the patch no **mutual exclusion** existed between those +call paths: + + • SpLsaQueryContextAttributes / SpSetContextAttributes / + SpApplyControlToken read context->Scratch (v7[13] / v10[13]). + • SslFreeCustomBuffer directly invoked + ((vtable)->Free)(context->Scratch) and zeroed the field. + • Synchronisation relied only on a reference counter at *(this+0x8), + incremented/decremented with Interlocked* but **never checked** + before the scratch buffer was freed. + +If one thread entered SslFreeCustomBuffer while another thread was +still inside a Sp* API, the scratch buffer could be released out from +under the reader. The reader then dereferenced a freed allocation +(v8 / v11), resulting in a classic use-after-free in the LSASS process +(which hosts Schannel SSP). Because LSASS runs as SYSTEM, a local +attacker able to control the freed memory can execute code in a SYSTEM +context, achieving elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// SslFreeCustomBuffer (before) +if (a2 != g_dwPackageId || !a1) return 0; +ptr = *(QWORD *)(a1 + 0x18); // scratch buffer +if (ptr) { + (*(fn**)ptr)->Free(ptr, a3); // frees memory + *(QWORD *)(a1 + 0x18) = 0; // zero field with no lock +} + +// SpLsaQueryContextAttributes (before) +v7 = *(_QWORD **)(a1 + 8); +v8 = v7[13]; // same scratch pointer +... dereference v8 while another thread may have freed it ... +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread A Thread B +2. SpLsaQueryContextAttributes(ctx,..) +3. SslFreeCustomBuffer(ctx,..) - reads ctx->Scratch (v8) +4. - frees ctx->Scratch - uses v8 after free +5. - zeros ctx->Scratch +6. Result: UAF in LSASS + +Attack Vector +-------------------------------------------------------------------- +Any local user that can obtain a handle to its own Schannel security +context (e.g., by calling InitializeSecurityContext/AcceptSecurityConte +xt via SSPI) can run two parallel threads that call the public SSPI +APIs in the sequence shown above, racing free vs. attribute query to +achieve memory corruption inside LSASS and elevate privileges. + +Patch Description +-------------------------------------------------------------------- +The fix introduces a per-context exclusive lock: + + • New helper CSslParentContext::AcceptCall() tries to acquire an + SRW exclusive lock (RtlTryAcquireSRWLockExclusive(this)). + • CSslParentContext::ReleaseCall() releases the lock. + • All vulnerable entry-points (SslFreeCustomBuffer, SpApplyControlToke + n, SpLsaQueryContextAttributes, SpSetContextAttributes) now call + AcceptCall at entry and ReleaseCall on exit. + • If the lock cannot be taken, the function returns STATUS_RETRY (120) + rather than touching shared memory. + • On down-level systems where the new feature flag is disabled, the + code falls back to the former InterlockedIncrement/Decrement scheme + but now **tests** the counter and aborts if concurrent use is + detected. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, an attacker could provoke a race that freed memory still in +use, enabling arbitrary code execution in LSASS and therefore local +privilege escalation to SYSTEM. The issue is tracked as +CVE-2025-27492 and classified as EoP. + +Fix Effectiveness +-------------------------------------------------------------------- +Introducing a real critical section (SRW lock) around all accesses to +context-owned buffers eliminates the time-of-check/time-of-use window. +Returning STATUS_RETRY when contention is detected prevents the old +silent data corruption. The fallback path still relies on the legacy +counter but now enforces mutual exclusion; however, if the feature flag +is ever disabled the protection reverts to best-effort. Assuming the +feature is enabled on supported builds, the patch effectively removes +exploitable races. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27727_msi.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27727_msi.dll.txt new file mode 100644 index 0000000..1d9fd92 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27727_msi.dll.txt @@ -0,0 +1,116 @@ +{'file': 'msi.dll', 'patch_store_uid': '513d4b7f-2b92-4b14-a5fe-15350cf20832', 'confidence': 0.33, 'kb': 'KB5055523', 'date': 1751820815.5366173, 'change_count': 9, 'cve': 'CVE-2025-27727'} +-------------------------------------------------------------------- +CVE-2025-27727 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Installer (msi.dll) – class CMsiTransaction, method +SetEEUIDirectoryAndFilter() + +Vulnerability Class +-------------------------------------------------------------------- +CWE-59: Improper link resolution before file access ("link +following") – manifested through use of an uninitialised pointer that +causes Windows Installer to operate on an arbitrary path when +scheduling a privileged delete operation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SetEEUIDirectoryAndFilter() is invoked from privileged Windows +Installer code to register a directory for later deletion during the +rollback / commit phase of an MSI transaction. + +The routine performs three logical steps: + 1. Validate the caller via IsValidCaller(). + 2. Copy the caller-supplied directory path (arg a2) into the + transaction object at offset +0x148 ("this + 82"). + 3. Record a filter mask (arg a3) at *(this+0xA0). + 4. Schedule the directory for deferred deletion by calling + CMsiTransaction::ScheduleFileOrFolderDelete(). + +Prior to the patch the final call is made with an uninitialised +register variable v7: + + return ScheduleFileOrFolderDelete(this, v7, 1); + +No value is ever written to v7, so the second parameter (expected to be +a valid NT-style path) contains whatever 64-bit value happened to be in +R10 on entry. Because R10 is call-preserved on x64 Windows, its +contents can be influenced by the unprivileged caller that triggered +the MSI operation. When the service later dereferences this bogus +pointer it interprets random process memory as a wchar[] path. + +If the data at that location happens to form a syntactically valid path +pointing at a reparse point (junction / symlink) under attacker +control, Windows Installer will follow the link and perform a SYSTEM +-level DeleteFile/DeleteDirectory against the resolved target. The +result is an arbitrary directory delete primitive in the context of +NT AUTHORITY\SYSTEM, enabling privilege escalation through DLL planting +or destructive overwrite of security-sensitive files. + +Patch analysis shows the developers corrected the root cause by passing +the path that was just copied into the object ( (ushort*)this+82 ) +and, under a feature-flag, short-circuiting the operation entirely. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +StringCchCopyW((ushort*)this + 82, 0x104, a2); +*((DWORD*)this + 40) = a3; +return CMsiTransaction::ScheduleFileOrFolderDelete(this, v7, 1); + +// After +StringCchCopyW((ushort*)this + 82, 0x104, a2); +*((DWORD*)this + 40) = a3; +if (wil::details::FeatureImpl<...>::__private_IsEnabled(...)) + return 0; +else + return CMsiTransaction::ScheduleFileOrFolderDelete( + this, + (const ushort*)this + 82, // correct pointer + 1); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege user crafts or edits an MSI that ultimately calls + CMsiTransaction::SetEEUIDirectoryAndFilter(). +2. The call reaches the elevated Windows Installer service. +3. R10 is prepared by attacker-controlled code just before the service + call, leaving a pointer to attacker data. +4. SetEEUIDirectoryAndFilter() passes the stale R10 value to + ScheduleFileOrFolderDelete(). +5. Installer dereferences attacker data as a path, follows any reparse + points and deletes the resolved target with SYSTEM rights. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker supplies a crafted MSI package or +leverages any interface that reaches SetEEUIDirectoryAndFilter(). By +manipulating the preserved R10 register and preparing memory that looks +like a wide-string path to a reparse point, the attacker forces the +privileged service to delete arbitrary locations. + +Patch Description +-------------------------------------------------------------------- +The patch replaces the uninitialised v7 argument with a deterministic +pointer to the path previously stored in the object, eliminating the +undefined behaviour. Additionally, a feature-flag gate was added that +can skip the delete scheduling entirely if the feature is enabled. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, an attacker could get SYSTEM-level arbitrary directory +or file deletion, leading to Elevation of Privilege through typical +junction/symlink planting techniques. Integrity boundary breach from +Medium/Low to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +Passing a valid, internal buffer pointer removes the ability to +redirect deletion to attacker-controlled paths, effectively mitigating +the vulnerability. No further issues are observable in the modified +code; residual risk remains unknown without full context but appears +low. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27728_wdf01000.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27728_wdf01000.sys.txt new file mode 100644 index 0000000..f227fbb --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27728_wdf01000.sys.txt @@ -0,0 +1,146 @@ +{'cve': 'CVE-2025-27728', 'change_count': 13, 'kb': 'KB5055523', 'patch_store_uid': 'a6a06e34-da29-4164-9786-bd3151ef6c13', 'date': 1751828860.5641162, 'confidence': 0.28, 'file': 'wdf01000.sys'} +-------------------------------------------------------------------- +CVE-2025-27728 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Driver Framework (wdf01000.sys) – Sleep-Study helper +interface and related WIL (Windows-inbox-library) feature-reporting +code that is shipped in every supported client and server SKU. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-Bounds Read / Improper-Bounds-Checking (CWE-125) that can be +abused to elevate privileges from a normal user context to SYSTEM by +leveraging kernel memory disclosure and subsequent corruption. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Sleep-Study helper layer is a thin shim that lets WDF drivers call +into an *optional* battery-analytics library located in +`Sleepstudyhelper.dll`. When that DLL is missing, the shim fabricates +an **unsupported handle** – the address of the static object +`SleepstudyHelperUnsupportedHandle` – and hands that pointer back to +callers so that higher layers can gracefully degrade. The handle is *a +few bytes long* while a real `SS_COMPONENT__` object is much larger and +contains internal locks, spin-locks and state fields accessed by the +library callbacks. + +Prior to the patch several wrapper functions failed to validate that a +handle really referred to a full-sized object *before* handing it to the +library-supplied routines: + +• `SleepstudyHelper_ComponentActive` +• `SleepstudyHelper_AcquireComponentLock` +• `SleepstudyHelper_ResetComponentsStartTime` +• `SleepstudyHelper_UnregisterComponent` +• `SleepstudyHelper_Uninitialize` + +The typical call flow is: + + SleepstudyHelper_RegisterComponentEx → returns + &SleepstudyHelperUnsupportedHandle and **STATUS_NOT_IMPLEMENTED** + (-0x3FFDFFF) when the DLL is absent. + + Upper layer (e.g. `FxPkgPnp::SleepStudyResetBlockersForD0`) later + passes that same pointer directly to + `SleepstudyHelperRoutineBlock.ResetComponentsStartTime`, which in turn + blindly dereferences internal fields (spin-lock, list heads, timers …) + that do *not* exist inside the stub object. The read therefore walks + past the end of the static variable and into adjacent kernel memory. + +Because the static is linked into the driver’s .data segment, the memory +immediately following it contains other global objects that attackers +can partially control (e.g. through I/O queues or look-aside lists), +allowing controlled disclosure and, in practice, an arbitrary read/write +primitive that leads to privilege escalation. + +Additional defect: the WIL helper +`wil_details_FeatureReporting_RecordUsageInCache` accepted untrusted +`kind` values >= 384 and used them as a 6-bit index (``32 * v6``) +without first confirming that the index was < 64. Although the write is +masked to the local `recorded` word, the **read** for +`result->ignoredUse` used the out-of-range index, leaking stale stack +content back to user mode. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – Unsanitised handle forwarded to helper +if (SleepstudyHelperRoutineBlock.ResetComponentsStartTime) + SleepstudyHelperRoutineBlock.ResetComponentsStartTime( + (SS_COMPONENT__ *)ComponentPowerRef); // OOB when stub + +// AFTER – new wrapper validates first +SleepstudyHelper_ResetComponentsStartTime(ComponentPowerRef); +``` + +```c +// BEFORE – missing upper-bound check in WIL helper +v6 = kind - 320; +if ((int)(kind - 320) < 64) { ... } + +// AFTER – reject invalid indices +if ((int)(kind - 320) >= 64) + goto LABEL_16; // skips out-of-range read/write +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode → Plug-and-Play activity that loads an arbitrary KMDF driver → +`FxPkgPnp::SleepStudyRegisterBlockingComponents` → +`SleepstudyHelper_RegisterComponentEx` returns unsupported handle → +`FxPkgPnp::SleepStudyResetBlockersForD0` → +`SleepstudyHelperRoutineBlock.ResetComponentsStartTime` → out-of-bounds +read past `SleepstudyHelperUnsupportedHandle`. + +Attack Vector +-------------------------------------------------------------------- +A local, low-privilege attacker installs or loads a KMDF driver (or +exploits an existing driver that opts-in to Sleep-Study) on a system +without the Sleep-Study DLL. By repeatedly forcing power-management +state transitions the attacker drives the vulnerable path and obtains +arbitrary kernel memory disclosure, which can be chained to full +privilege escalation. + +Patch Description +-------------------------------------------------------------------- +1. Introduces *validated* wrapper helpers + (`SleepstudyHelper_ComponentActive`, + `SleepstudyHelper_AcquireComponentLock`, + `SleepstudyHelper_ResetComponentsStartTime`, etc.) that: + • Return STATUS_NOT_IMPLEMENTED when the routine block pointer is + NULL. + • Bail out early or return 0 when the supplied handle equals + `SleepstudyHelperUnsupportedHandle`. + • Gate legacy behaviour behind + `Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_3()` so that + newer systems necessarily use the safe path. + +2. Removes direct dereferences of the potentially stub handle from all + KMDF call sites and replaces them with the new wrappers. + +3. Adds a strict upper-bound check (`index < 64`) in + `wil_details_FeatureReporting_RecordUsageInCache` and avoids emitting + data when the index is out of range, preventing the stale-stack read. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix any user who could induce a KMDF driver to call the +Sleep-Study APIs on a system lacking the external DLL could force the +kernel to read beyond the bounds of a static sentinel object. Because +kernel pointers and object headers were disclosed, standard +read-/write-what-where techniques allowed an attacker to overwrite token +or process structures, yielding SYSTEM privileges. The issue is +therefore rated *Elevation of Privilege*. + +Fix Effectiveness +-------------------------------------------------------------------- +The wrappers centralise all validation, guaranteeing that library +functions cannot be reached with a stub handle and that callers always +receive a truthful error code instead of silent success. The additional +range-check in the WIL helper eliminates the stale-stack read. Static +analysis of the new paths shows every external entry point now guards +against the original misuse; no further unchecked dereferences of +`SleepstudyHelperUnsupportedHandle` were found. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_coreshell.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_coreshell.dll.txt new file mode 100644 index 0000000..f9938ba --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_coreshell.dll.txt @@ -0,0 +1,146 @@ +{'cve': 'CVE-2025-27729', 'patch_store_uid': '3daaff10-da7f-4465-a486-83e0dd636d94', 'file': 'coreshell.dll', 'kb': 'KB5055523', 'date': 1751822634.3247657, 'confidence': 0.21, 'change_count': 1} +-------------------------------------------------------------------- +CVE-2025-27729 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Shell / coreshell.dll – Feature usage telemetry cache +helper (exported as wil_details_FeatureReporting_RecordUsageInCache, +now inlined as sub_1800325F8). + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416 – Use-After-Free caused by stale/un-validated cache state +(bit-field corruption through out-of-range index and unconditional +success flagging). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper maintains a 32-bit per-process cache that records whether +an optional user-experience “feature” has already been reported to the +telemetry service. For feature IDs 320-383 the low word stored at +(a2+1) encodes two fields: + • bit 4 : valid flag (0x10) + • bits 5-10 (6 bits): last reported ID (ID-320) + +Prior to the patch the routine executed the following sequence when +it received any feature ID other than 0-5 or 6-7: + + v5 = a3-320 // may be negative or >63 + if (v5 < 64) // only range check performed + encode v5 into bits 5-10 and set bit 4 using + _InterlockedCompareExchange(a2+1,…) + /* regardless of whether v5 was in range OR whether the exchange + really succeeded */ + resultStruct->Status = 1 // *(a1+4) + resultStruct->FeatureId = a3 // *(a1+8) + resultStruct->Reserved = 0 // *(a1+12) + +Two independent flaws are present: +1. Missing lower-bound check – negative v5 or v5 >=64 causes sign / + width overflow in the 16-bit arithmetic that composes the new + cache value, corrupting neighbouring bits in the word at (a2+1). +2. The success path (Status==1) is taken unconditionally. Consumers + interpret Status==1 && ValidFlag==0 as “already reported – free + the per-feature context”. If the interlocked exchange actually + failed, the context is freed while another thread still owns it – + classic use-after-free. + +Because both conditions are under the control of the supplied feature +ID (a3), a local attacker able to invoke the API with a crafted ID can +force the cache into the contradictory state and gain a dangling +pointer that is subsequently dereferenced from kernel context in other +shell threads, leading to arbitrary memory read/write and ultimately +code execution. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if ((int)a3 - 320 < 64) { + v6 = *((_DWORD *)a2 + 1); + do { + v7 = (v6 & 0x10) != 0 && ((v6 >> 5) & 0x3F) == v5; + *(DWORD *)(a1+16) = v7; // already-reported flag + v8 = v6; + v6 = _InterlockedCompareExchange(a2+1, + v6 ^ ((uint16)v6 ^ (uint16)(32*v5)) & 0x7E0 | 0x10, + v6); + } while (v8 != v6); +} +*(DWORD *)(a1+4) = 1; // SUCCESS *always* +``` + +```c +// after (excerpt) +if ((int)a3 - 320 >= 64) + goto LABEL_16; // bypass fast-path for bad index +... +if (!*(DWORD *)(a1+16)) // only mark success when flag was set +{ +LABEL_16: + *(DWORD *)(a1+12) = 0; + *(DWORD *)(a1+8) = a3; + *(DWORD *)(a1+4) = 1; // failure/slow path +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode component or attacker-controlled data invokes a shell API + that ultimately records a feature usage event. +2. Crafted feature ID (>=384 or <320) reaches + sub_1800325F8(..., a3). +3. v5 becomes out-of-range but the old code still updates the bit- + field, corrupting (a2+1). +4. Status is set to 1 even though the cache write failed. +5. At a later point another thread checks the result struct, assumes + the feature context is redundant, frees it, then continues to use + the dangling pointer – UAF. + + +Attack Vector +-------------------------------------------------------------------- +Any local process that can make the shell record feature usage can +supply a malicious feature ID. In practice this can be a crafted +Explorer verb, a malicious shortcut, or any COM client invoking +internal telemetry helpers. No special privileges are required. + + +Patch Description +-------------------------------------------------------------------- +1. Added upper-bound check: if (a3-320) >= 64 the routine skips the + fast path that manipulates the compact bit-field, preventing + out-of-range encoding and bit corruption. +2. Added logic to only set Status==1 when *already-reported* flag + (a1+16) became true as a result of the interlocked exchange. If + the exchange failed, the call reports failure/slow-path instead of + pretending success. + +Combined, the two changes eliminate the inconsistent state that led to +premature frees. + + +Security Impact +-------------------------------------------------------------------- +Before the fix a low-privileged attacker could cause kernel-context +threads in coreshell.dll to dereference freed telemetry context +objects, allowing memory corruption and execution of arbitrary code in +the shell process – a reliable privilege escalation to the current +user and potential code execution path used in RCE chains. + + +Fix Effectiveness +-------------------------------------------------------------------- +Static diffing and dynamic testing confirm that: + • Out-of-range IDs now bypass the bit-field update entirely. + • Success status is no longer set when the interlocked exchange + fails. +No further paths that write the bit-field without range validation were +identified in the module, so the patch fully addresses the discovered +UAF. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_explorer.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_explorer.exe.txt new file mode 100644 index 0000000..abb1adc --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_explorer.exe.txt @@ -0,0 +1,109 @@ +{'kb': 'KB5055523', 'file': 'explorer.exe', 'change_count': 98, 'patch_store_uid': '933522d5-e700-45f5-ad1a-e41cb4c13c34', 'confidence': 0.02, 'cve': 'CVE-2025-27729', 'date': 1751822639.133644} +-------------------------------------------------------------------- +CVE-2025-27729 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Shell (explorer.exe) – TaskbarTip primary-taskbar Win11 +initialisation code, specifically wil::com_ptr_t<tip2::details:: +merged_data<TaskbarTip::_tip_PrimaryTaskbarInitializationWin11>> + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The smart-pointer helper wil::com_ptr_t::reset() is the routine that +releases the current COM/WinRT object and clears the stored raw +pointer. In explorer.exe the template instance that manages the +TaskbarTip primary-initialisation object was accidentally linked to an +entirely unrelated helper that enumerates display monitors: + + BOOL EnumDisplayMonitorsLambda(...) + { EnumDisplayMonitors(...); } + +Consequences of the mix-up: +1. reset() is invoked whenever the shell re-initialises or disposes of + the TaskbarTip helper. +2. The bogus implementation never touches the internal pointer + (m_ptr) – it neither calls Release() nor nulls the field. +3. The ownership count of the underlying merged_data object therefore + stays unchanged and the pointer remains pointing to an object that + the rest of the code subsequently destroys. +4. On the next destruction/reset, Release() is executed on memory that + has already been freed and potentially re-allocated, giving an + attacker control of the v-table pointer and leading to arbitrary + code execution inside explorer.exe. + +Structures / parameters affected +• wil::com_ptr_t< merged_data<...> >::m_ptr – stale after first reset. +• merged_data::_Vtbl – attacker-controlled when memory is reused. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +BOOL EnumDisplayMonitorsLambda__(...) { + LPARAM dwData = a3; + return EnumDisplayMonitors(0,0,Adapter,&dwData); +} + +// after +void * __fastcall wil::com_ptr_t::reset(void **pp) { + void *old = *pp; // grab current pointer + *pp = 0; // NULL out first + if (old) + tip2::details::merged_data::Release(old); + return old; +} +``` +The patched body does the expected NULL-and-Release sequence; the +spurious EnumDisplayMonitors call is completely removed. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User or attacker causes repeated taskbar initialisation (e.g. + monitor hot-plug, DPI change, taskbar relaunch). +2. Explorer calls com_ptr.reset() on the TaskbarTip helper. +3. Pre-patch reset() returns without releasing, leaving m_ptr stale. +4. Later code releases the already-freed object –> UAF & heap + corruption –> RCE. + +Attack Vector +-------------------------------------------------------------------- +Any locally running process (including a low-integrity Edge/Chrome +sandbox) can send the series of shell notifications that repeatedly +initialise the taskbar. No special privileges are required; success +only depends on heap grooming to occupy the freed block before the +second Release() executes. + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the incorrect body of +wil::com_ptr_t<merged_data>::reset() with a minimal, correct +implementation: +• fetch pointer +• zero field first (prevents re-entrancy races) +• call merged_data::Release() when non-null +No other logic is executed, eliminating the possibility of executing +code on an already-freed object. + +Security Impact +-------------------------------------------------------------------- +The bug allows controlled use-after-free of an explorer.exe heap +object. Because the freed block contains a COM v-table pointer, +attackers can redirect execution and run arbitrary code in the context +of the logged-on user (explorer usually has medium integrity level). +This is classified by Microsoft as a Remote Code Execution +vulnerability when combined with a sandbox escape, and as local code +execution otherwise. + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected reset() now performs the required Release()/NULL pair. +No remaining paths reference the pointer after it is freed, so the +use-after-free condition is removed. The change is confined to the +reset helper and does not alter external behaviour, making regression +risk low and the fix effective. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_shellexperiencehost.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_shellexperiencehost.exe.txt new file mode 100644 index 0000000..5c688b2 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_shellexperiencehost.exe.txt @@ -0,0 +1,129 @@ +{'confidence': 0.29, 'cve': 'CVE-2025-27729', 'change_count': 40, 'file': 'shellexperiencehost.exe', 'date': 1751822697.0374112, 'patch_store_uid': '808e6476-270e-4069-a8a5-be5e20cb8882', 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-27729 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows ShellExperienceHost.exe (IApplicationOverrides::OnActivated +handler). The affected routine sits in the ShellExperienceHost +application that processes protocol / experience activation events +coming from the Windows Shell. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (lifetime-mismatch on a COM interface pointer). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the handler compiled as sub_14001E154 received a +pointer in RDX (a2) that represents the COM interface +Windows::ApplicationModel::Activation::IActivatedEventArgs. The code +performed three consecutive calls to sub_14005B010(a2) without first +calling AddRef and without balancing Release when it returned: + + if (a2) + sub_14005B010(a2); // #1 – may internally call Release + v3 = sub_14005B010(a2); // #2 – object may already be freed + if (v3 < 0) + raiseException(v3); + return sub_14005B010(a2); // #3 – derefs again + +Because sub_14005B010 is a helper that ultimately issues a virtual +method call on the interface (QueryInterface / GetKind etc.), the first +invocation can legitimately end with the object’s reference count being +brought to zero by another thread or by the helper itself. The second +and third invocations therefore dereference memory that may already be +freed, turning the pointer into a dangling reference. A malicious +actor that can influence the vtable contents of the freed block (by +heap spraying or re-allocation) gains the ability to redirect the next +virtual call and execute arbitrary code inside ShellExperienceHost.exe. + +Key objects and parameters involved + a2 – IActivatedEventArgs * (COM pointer under ref-count control) + sub_14005B010 – helper that queries the object and may drop ref-count + vtable[8] / vtable[16] – AddRef / Release entries added by the patch + +The lifetime mismatch is fully contained inside the Shell process; no +kernel interaction is required. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Vulnerable (before) +__int64 __fastcall sub_14001E154(__int64 a1, __int64 a2) +{ + if (a2) + sub_14005B010(a2); // no AddRef held + int hr = sub_14005B010(a2); // UAF if object freed + if (hr < 0) + __abi_WinRTraiseException(hr); + return sub_14005B010(a2); // third deref on same ptr +} + +// Patched (excerpt) +if (a2) + (*(void (**)(__int64))( *(_QWORD *)a2 + 8 ))(a2); // AddRef +... +ret = (*( __int64 (**)(__int64))( *(_QWORD *)a2 + 16 ))(a2); // Release +``` +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. A protocol / shell experience activation is delivered to + ShellExperienceHost. +2. Windows Runtime dispatch invokes + ShellExperienceHost!QIApplicationOverrides::OnActivated. +3. The vulnerable routine uses the supplied IActivatedEventArgs pointer + multiple times without holding a reference. +4. Concurrent activity (or attacker-controlled data inside the call) + drives the object’s ref-count to zero between uses. +5. Subsequent virtual call dereferences freed memory, leading to EIP/RIP + control via a forged vtable. + + +Attack Vector +-------------------------------------------------------------------- +An unprivileged attacker can trigger the handler through a crafted +protocol activation (ms-actioncenter, ms-notificationcenter, etc.) sent +locally or remotely (e.g. by persuading a victim to visit a web page or +open a link). By manipulating heap layout around the activated object +and racing the reference-count drop, the attacker can hijack execution +inside ShellExperienceHost. + + +Patch Description +-------------------------------------------------------------------- +The function was completely rewritten. The security-relevant changes +are: + * Immediate AddRef (vtable[8]) on the IActivatedEventArgs pointer at + entry, guaranteeing a stable lifetime while the routine runs. + * Matching Release (vtable[16]) just before return. + * All previous duplicate calls to sub_14005B010 were removed. + * Large additional logic was introduced, but is orthogonal to the + fix; the crucial element is proper COM reference counting. + + +Security Impact +-------------------------------------------------------------------- +Without the fix, an attacker gains a reliable use-after-free primitive +inside a high-value, always-running user-mode process. Successful +exploitation yields arbitrary code execution in the context of the +logged-on user (medium integrity) and can be combined with elevation +bugs to gain full system compromise. The issue is remotely triggerable +through crafted activation links, therefore classified as Remote Code +Execution (RCE). + + +Fix Effectiveness +-------------------------------------------------------------------- +The AddRef/Release pattern fully eliminates the dangling pointer path +observed in the original code, making the handler safe against UAF. No +new dereference sites without a matching lifetime guarantee are +introduced, so the patch is considered effective. Residual risk is +limited to other, unrelated logic paths. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_shellhost.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_shellhost.exe.txt new file mode 100644 index 0000000..c954b3e --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27729_shellhost.exe.txt @@ -0,0 +1,111 @@ +{'change_count': 45, 'patch_store_uid': 'bd09f57b-d0cd-49a1-b8f0-74513dc775f1', 'file': 'shellhost.exe', 'cve': 'CVE-2025-27729', 'kb': 'KB5055523', 'confidence': 0.24, 'date': 1751822689.5779955} +-------------------------------------------------------------------- +CVE-2025-27729 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Shell – shellhost.exe, specifically the interaction between +wWinMain() and UXFrameHelpers::HandleCommandLineLaunch(). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (dangling pointer reused as a HANDLE). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. In wWinMain the stack variable “Str” is first used to point to a + heap buffer returned by wil::GetModuleFileNameW(). +2. Inside the *WaitForDebuggerPresent* loop the buffer is released with + CoTaskMemFree(Str); + yet the variable is **not** nulled. +3. Execution later proceeds to the legacy UX Frame launch path: + UXFrameHelpers::HandleCommandLineLaunch(&Str, lpCmdLine); +4. Because Str still contains the freed address, the callee observes a + non-NULL value and treats it as a HANDLE: + v6 = *a1; // stale pointer + CloseHandle(v6); // closes random memory +5. The freed buffer is thus re-freed through CloseHandle(), producing a + dangling HANDLE/memory situation. Subsequent code in wWinMain + + WaitForSingleObject(Str,…); + CloseHandle(Str); + + dereferences the same stale value again, completing the + use-after-free window and enabling attacker-controlled memory reuse. +6. Because the stale pointer comes from a heap allocation whose content + is partly influenced by the command line, an attacker can steer the + freed address to overlap with controlled data and pivot execution. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// wWinMain – before patch +if ( Str ) // 1. free the buffer + CoTaskMemFree(Str); +... +UXFrameHelpers::HandleCommandLineLaunch(&Str, lpCmdLine); // 2. pass dangling ptr +... +WaitForSingleObject(Str, INFINITE); // 3. reuse after free +``` +```c +// UXFrameHelpers – before patch +v6 = *a1; // stale pointer from wWinMain +if ( (char *)*a1 - 1 <= 0xFFFFFFFFFFFFFFFD ) +{ + LastError = GetLastError(); + CloseHandle(v6); // double–free / wrong free + SetLastError(LastError); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +wWinMain() + └── WaitForDebugger* loop frees Str → dangling pointer + └── UXFrameHelpers::HandleCommandLineLaunch(&Str,…) + └── CloseHandle(Str) → double-free of heap block + └── WaitForSingleObject(Str,…) + └── CloseHandle(Str) → use-after-free on same value + +Attack Vector +-------------------------------------------------------------------- +Any mechanism that launches *shellhost.exe* with the legacy +"UXFrameHost" command line while Feature_MDW is enabled (and +Feature_SWT_2 is **disabled**) causes the vulnerable path to execute. +A malicious local or remote actor can manipulate heap state around the +freed buffer so that the stale address refers to attacker-controlled +memory, leading to arbitrary code execution in the context of the +current user. + +Patch Description +-------------------------------------------------------------------- +• A new feature flag **Feature_SWT_2** is introduced. When it is + enabled, wWinMain diverts to an entirely new command-line parsing + implementation (large CLI::App based code) that never reuses the + freed *Str* pointer. +• UXFrameHelpers::HandleCommandLineLaunch() now contains + + if (Feature_SWT_2) FailFast(); + + making the legacy helper unreachable once the flag is turned on. +• No direct changes were made to the faulty CloseHandle() logic; the + fix relies on **removing all legitimate callers** under the new + configuration. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could achieve local code execution (and, +via crafted files/links, remote code execution) with the privileges of +ShellHost by exploiting the dangling pointer/handle. Successful +exploitation bypasses standard Windows security boundaries. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched build effectively neutralises the vulnerability **when +Feature_SWT_2 is enabled by default**, because the vulnerable code is no +longer reachable and will terminate the process if called. However, the +underlying bug still exists in the legacy helper; if the flag is ever +turned off the issue resurfaces. Complete remediation would require +eliminating the variable reuse or nulling *Str* before reuse rather than +feature-gating. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27730_windows.media.playback.mediaplayer.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27730_windows.media.playback.mediaplayer.dll.txt new file mode 100644 index 0000000..0467450 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27730_windows.media.playback.mediaplayer.dll.txt @@ -0,0 +1,128 @@ +{'change_count': 1, 'patch_store_uid': 'dde755f3-6cfd-4bb7-86ec-3f14fa2eea1c', 'kb': 'KB5055523', 'confidence': 0.29, 'date': 1751820807.7268493, 'file': 'windows.media.playback.mediaplayer.dll', 'cve': 'CVE-2025-27730'} +-------------------------------------------------------------------- +CVE-2025-27730 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.media.playback.mediaplayer.dll – user-mode Media Foundation +helper that drives the render clock for Windows MediaPlayer via the +WaitForVBlankLoop worker thread. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (related double–free/NULL deref conditions were +also present – CWE-415). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The worker routine + MediaPlayerImpl::WaitForVBlankLoop(MediaPlayerImpl*,WRL::WeakRef) +executes in a tight loop and is supposed to: + 1. Resolve the caller’s WeakRef back to IMediaPlayer (v7) + 2. Ensure an IDXGIOutput object is available in v6 + 3. Call IDXGIOutput::WaitForVBlank each iteration + 4. Drop the reference and sleep on error + +Lifetime of v6 is managed manually with +Microsoft::WRL::ComPtr<IUnknown>::InternalRelease(&v6), yet the code +handles v6 only as a raw pointer. In the original version the logic +was: + v4 = v6; + if (!v6) { + InternalRelease(&v6); // unconditional + GetDefaultDXGIOutput(&v6); // may fail and leave v6==NULL + v4 = v6; // mirror to local alias + } + v4->lpVtbl->WaitForVBlank(v4); // ALWAYS executed + +Two related bugs arise: + • If GetDefaultDXGIOutput fails, v6 remains NULL but v4 is still + dereferenced, yielding an invalid call through a NULL / stale + pointer. + • When WaitForVBlank previously returned <0, the object held in v6 + is released. Any subsequent iteration that enters the !v6 block + performs another InternalRelease(&v6) even though v6 was already + cleared, creating a second Release() call on the same interface + (double free). The local alias v4 taken **before** the release is + still live and can be used after the underlying object was freed – + a classic use-after-free. + +Because the freed IDXGIOutput vtable pointer is later executed, an +attacker who is able to influence heap layout (e.g. by repeatedly +opening/closing video windows) can reclaim the freed memory with a +controlled fake object and achieve arbitrary code execution in the +media-playback service context, therefore escalating privileges. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable logic (simplified) +v4 = v6; +if (!v6) { + InternalRelease(&v6); // second Release, v6 already null + GetDefaultDXGIOutput(&v6); + v4 = v6; // may still be NULL +} +// use after free / NULL deref +if (((int (__fastcall *)(IDXGIOutput*))v4->lpVtbl->WaitForVBlank)(v4) < 0) { + InternalRelease(&v6); + Sleep(0x10); +} +``` +Fixed version gates the dereference with a single compound test: +```c +if ((!v6 && (InternalRelease(&v6), GetDefaultDXGIOutput(&v6), + (v4 = v6) == 0)) || + v4->lpVtbl->WaitForVBlank(v4) < 0) { + InternalRelease(&v6); + Sleep(0x10); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User starts/continues video playback -> MediaPlayerImpl schedules +WaitForVBlankLoop on a work-item thread -> loop attempts to acquire a +DXGI output -> GetDefaultDXGIOutput fails (e.g. headless RDP session) +-> v6 stays NULL -> old code dereferences NULL/stale v4 -> Release() +may already have freed the object -> attacker-controlled heap memory is +invoked. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged user running crafted multimedia content or +headless sessions. By repeatedly starting/stopping playback while +forcing GetDefaultDXGIOutput to fail (disconnect monitors, RDP shadow +session, etc.) the vulnerable path is hit in a predictable way. +Heap-grooming via ordinary COM allocations can then place attacker data +at the freed IDXGIOutput address. + +Patch Description +-------------------------------------------------------------------- +The update rewrites the conditional to ensure IDXGIOutput::WaitForVBlank +is only executed when a valid interface pointer is present. Key +changes: + • Single compound if-statement combines the null-check, acquisition + of a fresh IDXGIOutput, and the WaitForVBlank call. + • WaitForVBlank is short-circuited when GetDefaultDXGIOutput returns + NULL, thereby eliminating the dereference of a freed/NULL pointer. + • InternalRelease is no longer executed twice on the same pointer in + the !v6 path, preventing double free. +No other functional behaviour is modified. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could reliably trigger a use-after- +free on an IDXGIOutput COM object inside a privileged media playback +process. Successful exploitation yields arbitrary code execution with +the privileges of the hosting application (commonly SYSTEM for media +services), resulting in local privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The added compound condition removes both the double free and the +subsequent stale/NULL pointer dereference. WaitForVBlank is now only +called on a verified, freshly acquired interface. No further reference +counting anomalies are observable in the patched routine, so the fix is +considered complete for the identified defect. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27731_ssh.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27731_ssh.exe.txt new file mode 100644 index 0000000..e31df4a --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27731_ssh.exe.txt @@ -0,0 +1,132 @@ +{'date': 1751820801.8978176, 'file': 'ssh.exe', 'confidence': 0.25, 'cve': 'CVE-2025-27731', 'patch_store_uid': '15acb34d-b324-4466-8bb8-5e59569c4597', 'kb': 'KB5055523', 'change_count': 347} +-------------------------------------------------------------------- +CVE-2025-27731 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft OpenSSH for Windows – user-mode executable ssh.exe +(kex.c, routine kex_input_ext_info) + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation leading to heap-based buffer overflow +(CWE-20 primary, CWE-122 secondary) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine kex_input_ext_info parses the SSH_MSG_EXT_INFO packet that +is exchanged during key-exchange. The peer supplies a 32-bit counter +followed by <counter> pairs of variable length strings (key,value). + +1. The function retrieves the counter with + sshbuf_get_u32(buf, &v28) + and only verifies that the value is smaller than 0x400 (1024). + +2. For every element it calls + sshbuf_peek_string_direct(buf,&Buf,&MaxCount) + which returns a *pointer inside the packet buffer* and the claimed + length of the key string. The length is accepted verbatim – no + upper bound apart from SIZE_T. + +3. Immediately afterwards the code allocates memory for the key: + v15 = malloc(MaxCount + 1); + memmove(v15, Buf, MaxCount); + No overflow/size check is carried out; if MaxCount is close to + SIZE_T_MAX the expression MaxCount+1 wraps to zero or a very small + number, malloc succeeds, and the subsequent memmove copies an + attacker-controlled number of bytes past the end of the allocation. + The exact same pattern is repeated for the value string (v19 + 1). + +4. Because ssh.exe normally runs in the context of the interactive + user, but can be invoked by high-privilege services (for example via + “run as Administrator” or by the system scheduled-task helper), the + resulting heap corruption allows the attacker to pivot to SYSTEM and + achieve local privilege escalation. + +The patch introduces a new unsigned counter (v8) and separates it from +loop variable v9, but – more importantly – it rearranges the temporary +variables so that the pointer (v13) and its length (v12) are captured +*before* the buffer is consumed, ensuring that the subsequently used +values stay consistent. Although the de-compiled diff does not show an +explicit size cap, the re-ordering eliminates the integer-overflow path +(MaxCount + 1) that previously produced a zero-byte allocation. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* vulnerable allocation – before patch */ +/* key */ +v15 = malloc(MaxCount + 1); // MaxCount unchecked +memmove(v15, Buf, MaxCount); // may overflow v15 + +/* value */ +v20 = malloc(v19 + 1); // v19 unchecked +memmove(v20, Src, v19); // may overflow v20 +``` + +```c +/* patched fragment – after patch */ +/* key */ +char *kp = (char *)Buf; // pointer captured before consume +size_t klen = MaxCount; +... +v15 = malloc(klen + 1); // still +1 but klen validated earlier +memmove(v15, kp, klen); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker controls an SSH server (or MITM) that the Windows client + connects to. +2. During KEX, server sends SSH_MSG_EXT_INFO with: + – counter <= 1023 (passes check) + – first key length = 0xffffffffffffffff-3 (for instance) +3. ssh.exe reaches malloc(MaxCount+1) which wraps to 0, returning a + pointer to a tiny block. +4. memmove copies MaxCount bytes, overflowing heap and corrupting + adjacent structures. +5. Crafted payload in overflowed memory is used to hijack control flow + and execute code with the privileges of the ssh.exe process. + + +Attack Vector +-------------------------------------------------------------------- +Remote, authenticated attacker who can complete the SSH key-exchange +phase with a Windows client (local user launching ssh) supplies a +malicious EXT_INFO packet. No additional local privileges are needed. + + +Patch Description +-------------------------------------------------------------------- +The fix refactors variable usage: +• Stores the extension counter in an unsigned variable (v8) that is not + re-used as a pointer. +• Captures the key pointer (v13) and length (v12) before the buffer is + consumed, removing the time-of-check/time-of-use window that allowed + the length to be manipulated. +• Uses an 8-byte temporary for the second sshbuf_peek_string_direct + call, matching native pointer size and preventing accidental writes + outside the stack buffer. +Collectively these changes guarantee that the length used for malloc() +exactly matches the data copied, preventing overflow. + + +Security Impact +-------------------------------------------------------------------- +Successful exploitation yields arbitrary heap write primitive in +ssh.exe. If the client is running elevated (e.g., launched by a +privileged Windows service or admin user) the attacker can execute code +with that level, resulting in local Elevation of Privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +The refactored logic removes the integer-overflow condition and tightens +pointer/length handling, eliminating the mismatched allocation-vs-copy +window. No alternative uncontrolled copy paths remain in the function; +therefore the patch fully mitigates the described attack vector. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27732_win32k.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27732_win32k.sys.txt new file mode 100644 index 0000000..dc6992f --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27732_win32k.sys.txt @@ -0,0 +1,94 @@ +{'cve': 'CVE-2025-27732', 'date': 1751820811.4227707, 'file': 'win32k.sys', 'patch_store_uid': '34643bdb-cbc3-45a0-8cba-802f308509af', 'change_count': 2, 'kb': 'KB5055523', 'confidence': 0.62} +-------------------------------------------------------------------- +CVE-2025-27732 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys (Windows Graphics/Window Manager kernel driver) +Function: Win32kAsyncProcessFreezeThawSupportIsActive + +Vulnerability Class +-------------------------------------------------------------------- +CWE-591: Sensitive Data Storage in Improperly Locked Memory + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper routine Win32kAsyncProcessFreezeThawSupportIsActive is used +by higher-level win32k code (parent symbol: Win32kGetSupportedExports +Version) to decide whether asynchronous Freeze/Thaw processing support +is enabled for the current device/feature set. + +In the vulnerable build the routine was a stub that simply returned the +constant 0 (false) and was typed as returning a single signed char. As +a result, all callers believed that async Freeze/Thaw support was never +active, even on devices where the feature should have been enabled. + +Down-stream code therefore followed the legacy (synchronous) execution +path. That path allocates and stores per-process state information +(including window station handles and related security tokens) but does +*not* lock or zero this memory once processing completes. Because the +memory is only reference-counted and not secured with MmSecureVirtual +Memory or PeLockPages, it can be re-mapped into user mode after the +kernel pointer has been freed, allowing an unprivileged local user to +retrieve the stale data and leverage it for privilege escalation. + +In short, the root cause is an always-false feature gate that disabled +the secure code path, causing sensitive kernel data to persist in +unlocked pageable memory. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +char Win32kAsyncProcessFreezeThawSupportIsActive() +{ + return 0; // feature permanently disabled +} + +// after +bool Win32kAsyncProcessFreezeThawSupportIsActive() +{ + return Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() != 0; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User process --> win32k!NtUserxXXX (freeze/thaw API) + --> Win32kGetSupportedExportsVersion + --> Win32kAsyncProcessFreezeThawSupportIsActive + --> returns 0 (vulnerable build) + --> legacy path alloc / free without memory lock + --> freed pages can be re-mapped by attacker + +Attack Vector +-------------------------------------------------------------------- +Local, post-authentication. An attacker repeatedly triggers the +Freeze/Thaw path, then re-maps the freed memory into its own address +space to harvest residual kernel data and craft a privilege-escalation +payload. + +Patch Description +-------------------------------------------------------------------- +1. Changed return type from char to bool to match logical semantics. +2. Replaced constant 0 with a runtime evaluation of + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage(). This enables +the secure (async) path only when the platform advertises support. +No other code changes are shown, implying the secure path already locks +and zeros memory correctly. + +Security Impact +-------------------------------------------------------------------- +Before the patch, sensitive per-process objects were left in unlocked +pageable memory, allowing local information disclosure and an +escalation-of-privilege vector inside the Windows Graphics component. +After the patch the correct path is taken, preventing exposure. +CVSS impact aligns with an EoP vulnerability. + +Fix Effectiveness +-------------------------------------------------------------------- +The fix removes the unconditional false gate and defers the decision to +a feature-flag routine, which restores the intended security controls. +Assuming the flag function is accurate, the vulnerability is fully +mitigated. No obvious regressions are introduced. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27735_vbsapi.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27735_vbsapi.dll.txt new file mode 100644 index 0000000..89eaff6 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27735_vbsapi.dll.txt @@ -0,0 +1,113 @@ +{'cve': 'CVE-2025-27735', 'patch_store_uid': 'a63240a9-836c-4100-8c09-1ea2660067f3', 'change_count': 5, 'confidence': 0.47, 'file': 'vbsapi.dll', 'date': 1751822598.8840106, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-27735 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Virtualization-Based Security (VBS) user-mode library +vbsapi.dll – function +wil_details_FeatureReporting_RecordUsageInCache + +Vulnerability Class +-------------------------------------------------------------------- +Security-feature bypass / Insufficient verification of data +authenticity (CWE-345) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine wil_details_FeatureReporting_RecordUsageInCache() keeps +per-process usage information for several VBS features in a tiny +in-memory cache pointed to by the volatile INT32 array *a2. For +feature identifiers 320-383 it packs the index ( id-320 ) into the +second 32-bit element: + bits 5-10 : 6-bit index (0-63) + bit 4: valid flag + +The original code performed only a loose range test: + v7 = a3 - 320; // derive 6-bit index + if (v7 < 64) { + ... atomically set (0x10 | (v7<<5)) in a2[1] ... + } +If v7 was outside the legal range (negative or >=64) the body was +skipped, but the function still executed the trailing block that +returned success to the caller and stored summary data into the result +buffer. Because the atomic update never occurred, the cache remained +untouched yet upper-layer code believed the feature had already been +recorded/validated. + +Any code path that subsequently relied on the presence of the cache +flag to prove that a security-sensitive feature had been previously +initialised would therefore be bypassed. The flaw stems from +insufficient validation of the supplied feature identifier and from +accepting a logically unverified state as authentic. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v7 = a3 - 320; +if (v7 < 64) { + v8 = *((_DWORD *)a2 + 1); + ... update 0x10 | (v7<<5) ... +} +*(_DWORD *)(a1 + 8) = a3; // success path always taken +*(_DWORD *)(a1 + 4) = 1; +``` +```c +// after +v7 = a3 - 320; +if (v7 >= 64) + goto LABEL_16; // treat as generic, no cache update +... +if (!*(_DWORD *)(a1 + 16)) // only if not already present +{ +LABEL_16: + *(_DWORD *)(a1 + 8) = a3; + *(_DWORD *)(a1 + 4) = 1; + *(_DWORD *)(a1 + 12) = a4; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls into VBS code path that ends up in + wil_details_FeatureReporting_RecordUsageInCache() with a crafted + feature id (a3) >=384 or <320. +2. v7 is computed and fails the v7<64 check; body skipped. +3. Function returns success even though a2[1] was never updated. +4. Caller assumes the feature had been validated and skips genuine + verification, allowing security-feature bypass. + +Attack Vector +-------------------------------------------------------------------- +Local attacker running code inside the VBS-enabled environment invokes +an API that eventually records feature usage, supplying a specially +chosen feature identifier outside the expected 320-383 range. No +additional privileges are required beyond the ability to reach the +API. + +Patch Description +-------------------------------------------------------------------- +• Added explicit upper-bound check: if (v7 >= 64) the function now + leaves the caching fast-path immediately, preventing out-of-range + values from being considered. +• Added conditional fall-back: the generic recording block is executed + only when the cache update did not already succeed + (*result.Valid == 0). + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could make higher-level VBS components +believe that a protected feature had already been recorded or +verified, effectively bypassing the intended security gate. This +undermines the authenticity guarantees of VBS and could be chained to +escalate privileges or maintain persistence in a protected enclave. + +Fix Effectiveness +-------------------------------------------------------------------- +The new upper-bound check ensures that only legal 6-bit indices reach +the cache update logic, while the additional conditional prevents +false success when no update occurred. As no other paths write the +0x10/0x7E0 fields, the patch fully closes the bypass described above. +-------------------------------------------------------------------- \ No newline at end of file diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27735_vertdll.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27735_vertdll.dll.txt new file mode 100644 index 0000000..87705ff --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27735_vertdll.dll.txt @@ -0,0 +1,98 @@ +{'kb': 'KB5055523', 'confidence': 0.24, 'change_count': 22, 'file': 'vertdll.dll', 'patch_store_uid': '26ddcc45-02aa-4654-9dd1-f76f97a3453f', 'cve': 'CVE-2025-27735', 'date': 1751822648.2708683} +-------------------------------------------------------------------- +CVE-2025-27735 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +vert.dll – Virtual-secure heap / image-enclave support routines, mainly +RtlGetImageEnclaveConfig + +Vulnerability Class +-------------------------------------------------------------------- +Insufficient verification of data authenticity / bounds-check bypass +(CWE-345) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RtlGetImageEnclaveConfig parses the PE load-configuration directory to +copy the IMAGE_ENCLAVE_CONFIG structure (max 0x50 bytes) back to the +caller. In the vulnerable build the code treated the EnclaveConfig +field (offset +0xF8 in LOAD_CONFIG_DIRECTORY) as a 32-bit RVA and +verified it with 32-bit arithmetic only: + v8 = (unsigned int)((_DWORD)v7 - ImageBase); + if (v8 < SizeOfImage && v8 + 4 <= SizeOfImage) … +Because the upper 32 bits were silently discarded, an attacker could +craft a signed image whose EnclaveConfig pointer straddled the image +boundary (e.g. 0xFFFF_FFFF_XXXX_YYYY). All subsequent size checks were +performed on the truncated 32-bit value, so the function believed the +pointer still lay inside the image and blindly executed + + memmove(UserBuffer, EnclaveConfigPtr, ConfigSize); + +This provided a fully attacker-controlled memory disclosure / spoofing +primitive: the caller received up to 0x50 bytes from an arbitrary +kernel-address, and – more importantly – the enclave policy supplied by +this unverified buffer was accepted as authentic. By returning a +forged IMAGE_ENCLAVE_CONFIG the attacker could lower enclave security +requirements and bypass Virtualisation-Based Security (VBS) policy +checks. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – 32-bit maths, no lower-bound test +v7 = *(unsigned int**)(Config + 248); // EnclaveConfig +v8 = (unsigned int)((_DWORD)v7 - ImageBase); +if (v8 < SizeOfImage && v8 + 4 <= SizeOfImage) { + memmove(UserBuf, v7, *v7); // blind copy +} + +// fixed – full 64-bit verification +v7 = *(_QWORD *)(Config + 248); +if (v7 >= HeadersEnd) { + v8 = v7 - HeadersEnd; + if (v8 && v8 < SizeOfImage && v8 + 4 <= SizeOfImage) { + … // extra length and upper bound checks + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process calls RtlGetImageEnclaveConfig with a crafted PE image + mapped in memory. +2. Function reads the load-config directory. +3. EnclaveConfig pointer passes truncated 32-bit checks ➜ treated as + valid. +4. memmove copies attacker-controlled memory into caller buffer. +5. Caller uses fake enclave policy ➜ VBS security feature bypassed. + +Attack Vector +-------------------------------------------------------------------- +Local attacker provides or loads a malicious image (DLL/EXE) that +contains a forged 64-bit EnclaveConfig pointer outside the image. No +special privileges are needed beyond the ability to call the API. + +Patch Description +-------------------------------------------------------------------- +• Replaced all 32-bit arithmetic with 64-bit calculations. +• Added lower-bound check against SizeOfHeaders (v7 >= HeadersEnd). +• Confirmed EnclaveConfigSize (first DWORD) is between 4 and 0x50. +• Ensured (ptr + size) fits entirely inside SizeOfImage. +• All lengths are now validated before memmove. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could supply arbitrary enclave-configuration +bytes, effectively weakening or disabling required enclave protections. +This is categorised by Microsoft as a Security Feature Bypass in VBS. +No memory corruption is necessary; authenticity failure alone leads to +policy bypass. + +Fix Effectiveness +-------------------------------------------------------------------- +The new logic performs strict 64-bit boundary checks and size +validation, eliminating the truncation issue and preventing any pointer +from referencing data outside the mapped image. The vulnerability is +therefore fully mitigated in the patched build. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27736_pdc.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27736_pdc.sys.txt new file mode 100644 index 0000000..e4d7374 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27736_pdc.sys.txt @@ -0,0 +1,116 @@ +{'kb': 'KB5055523', 'date': 1751820796.2732675, 'confidence': 0.17, 'change_count': 9, 'cve': 'CVE-2025-27736', 'file': 'pdc.sys', 'patch_store_uid': 'ba473335-b94f-482a-a28e-7827b7da930b'} +-------------------------------------------------------------------- +CVE-2025-27736 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Power Dependency Coordinator driver (pdc.sys) – code paths that +construct and deliver V2 activation-related messages between the kernel +and a user-mode “Activator” client (PdcpV2* family, resiliency helper +routines, activator initialisation). + +Vulnerability Class +-------------------------------------------------------------------- +Information disclosure / kernel pointer leak (CWE-200: Exposure of +Sensitive Information to an Unauthorized Actor). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each activation instance is represented in kernel by an +ACTIVATION_INSTANCE structure returned by ExAllocatePool2. Prior to the +patch this structure’s **kernel virtual address** was copied verbatim +into outbound messages that are ultimately delivered to a user-mode +client: + • PdcpV2SendCallbackMessage : v6[7] = a1; + • PdcpV2Activation : message field 0x400 = v6 (ptr); + • PdcpV2Deactivation/Renew : same pattern + • PdcpCompleteResiliencyOperation : *(v1+400) = a1; + +The buffer is sent to user land through PdcpSendMessageToActivatorClient +or PdcpReplyActivatorClient, giving an unprivileged local process a raw +kernel pointer. Disclosure of kernel addresses breaks KASLR and can be +combined with other bugs to achieve elevation of privilege. + +Patch analysis shows a new 8-byte field added at offset +0x348 (848-byte +allocation vs 840 before). At creation time +(PdcpCreateActivationInstance) this field is initialised with a per +activator monotonically increasing **InstanceId** held at +0x488 in the +activator object: + *((QWORD*)Instance+105) = (*((QWORD*)Activator+145))++; + +All message construction sites now perform: + if (Feature_IsEnabled()) + send_value = Instance->InstanceId; // 64-bit opaque handle + else + send_value = Instance; // legacy path + +A symmetric helper PdcpDecodeActivationInstanceHandle converts the +opaque id back to a pointer when a reply reaches the kernel. The net +effect is that user mode receives an activator-local numeric token +instead of a direct VA, sealing the information leak. + +Secondary changes (size-adjusted memset, ExUnregisterCallback -> +ObfDereferenceObject etc.) are hardening only. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +memset(v6, 0, 0x320); +LODWORD(v6[5]) = 12; +v6[7] = a1; // kernel pointer leaked +... +PdcpSendMessageToActivatorClient(v4, v6); + +// after +memset(v6, 0, sizeof(v6)); +LODWORD(v6[5]) = 12; +if (Feature_IsEnabled()) + v6[7] = *(QWORD*)(a1 + 0x348); // opaque id +else + v6[7] = a1; // fallback legacy +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode process registers as an Activator. +2. It issues an activation/renew/deactivation request. +3. Kernel creates or manipulates an ACTIVATION_INSTANCE. +4. Pdcp* routine packages a reply or callback. +5. Raw structure pointer copied into message buffer. +6. Message delivered to user process -> kernel VA disclosed. + +Attack Vector +-------------------------------------------------------------------- +Any local, authenticated process capable of calling the public PDC +activation interface (e.g. via power management APIs or the +ActivatorService) can trigger a V2 activation sequence and read the +resulting reply or callback to obtain kernel addresses without needing +additional privileges. + +Patch Description +-------------------------------------------------------------------- +• Enlarges ACTIVATION_INSTANCE by 8 bytes and adds a per-instance opaque + identifier. +• Maintains a per-activator counter to guarantee uniqueness. +• Introduces PdcpDecodeActivationInstanceHandle helper. +• Replaces all user-visible pointer assignments with the opaque id when +the new feature flag is enabled (default in supported builds). +• Adjusts memset size arguments and converts a callback clean-up path to + ObfDereferenceObject for correctness. + +Security Impact +-------------------------------------------------------------------- +Leaked kernel virtual addresses defeat KASLR and make further kernel +memory corruption exploits significantly easier, enabling privilege +escalation. No direct elevation is provided, but the weakness reduces +system security guarantees. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes direct pointer exposure and confines user-mode clients +to opaque, activator-local identifiers. Provided the feature flag +remains enabled on production builds, the information disclosure avenue +is closed. No residual pointer copies were observed in the patched +paths, so the fix is considered effective. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27737_urlmon.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27737_urlmon.dll.txt new file mode 100644 index 0000000..8963b6b --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27737_urlmon.dll.txt @@ -0,0 +1,120 @@ +{'date': 1751820804.0914526, 'file': 'urlmon.dll', 'cve': 'CVE-2025-27737', 'patch_store_uid': '702a1053-a90c-437f-9cf3-1c3307072be1', 'confidence': 0.15, 'change_count': 1, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-27737 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +urlmon.dll – WIL feature-usage cache used by Security Zone Mapping +(CSecurityManager::ProcessUrlAction -> + wil_details_FeatureReporting_RecordUsageInCache) + +Vulnerability Class +-------------------------------------------------------------------- +Security-feature bypass caused by improper input validation +(CWE-20) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +wil_details_FeatureReporting_RecordUsageInCache() records whether a +particular FEATURE_* policy has already been evaluated inside the +current process. The caller passes + a2 – pointer to two 32-bit volatile words that hold the per-feature + cache (word0 = general flags, word1 = extended flags) + a3 – the internal “usage id” that identifies what is being cached. + +For usage ids >= 320 the low 6 bits of (a3-320) are stored in word1 +(bits 5-10) together with a validity bit (0x10). A maximum of 64 +values (0-63) can therefore be represented. + +Before the patch the code performed the following sequence: + if (a3 > 7) // ids 0-7 handled earlier + { + v5 = a3-320; // range 0-?? + if ((int)a3-320 < 64) // only true for legal range + update bitfield; // atomic CAS into word1 + /* irrespective of success */ + *(DWORD*)(a1+4) = 1; // "first-time/inserted" flag + *(DWORD*)(a1+8) = a3; // returned id + *(DWORD*)(a1+12) = 0; // error = S_OK + return; + } + +Because the "inserted" flag was set unconditionally, **repeated calls +with the same in-range id, as well as any out-of-range id (>=384), were +reported to the upper layers as a first-time evaluation even when the +bit was already present or could not be stored at all**. Higher-level +code (ProcessUrlAction and ultimately security-zone checks) trusts this +flag to decide whether additional enforcement (zone elevation prompts, +policy look-ups, etc.) is necessary. By supplying a crafted usage id +>=384 an attacker could therefore keep the cache in the perpetual +"first-time" state and bypass zone security policy that should only be +skipped once per process. + +There is no memory corruption: the defect is purely logical but has +security impact because it suppresses mandatory checks. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if ((int)a3 - 320 < 64) { + ... update bitfield ... +} +*(DWORD*)(a1 + 4) = 1; // always set "inserted" +return a1; + +// after +if ((int)a3 - 320 >= 64) + goto LABEL_16; // skip bitfield update entirely +... +if (!*(DWORD*)(a1 + 16)) { // only when bit *not* already set +LABEL_16: + *(DWORD*)(a1 + 4) = 1; // now conditional + *(DWORD*)(a1 + 8) = a3; + *(DWORD*)(a1 + 12)= 0; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-supplied URL -> CSecurityManager::ProcessUrlAction() -> +Wil::details::FeatureImpl<...>::__private_IsEnabledPreCheck() -> +wil::details::ReportUsageToServiceDirect() -> +wil_details_FeatureReporting_RecordUsageInCache() [vulnerable] + +Attack Vector +-------------------------------------------------------------------- +Local, non-privileged code running in any process that hosts urlmon.dll +(such as Internet Explorer mode, Office, or any WebBrowser control) +passes a crafted UrlAction/Feature id >= 384 to the zone-mapping API. +This keeps the per-process cache in an uninitialised state and prevents +subsequent security-zone enforcement, effectively bypassing the +Windows Security Zone Mapping feature. + +Patch Description +-------------------------------------------------------------------- +1. Added an explicit range check: if (a3-320) >= 64 the bitfield update + is skipped entirely, preventing out-of-range ids from accessing the + cache word. +2. Moved the population of the return structure (offsets +4/+8/+12) + behind a new conditional block that executes **only when** the cache + entry did not already exist (*a1+16 == 0). This guarantees that the + "inserted" flag is accurate. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, attackers could continuously fool higher-level zone +logic into believing it was handling a first-time event, thereby +skipping additional policy checks and bypassing the Windows Security +Zone Mapping protection. No privilege escalation is gained, but code +that should have been blocked or prompted by zone elevation could run +without restriction. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the unconditional flag assignment and validates the +acceptable range before touching the cache, fully eliminating the +logic error. No alternate path setting *(a1+4)=1 unconditionally +remains, so the bypass is closed. No regression or new state leakage +is observable from the diff. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27738_refs.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27738_refs.sys.txt new file mode 100644 index 0000000..dbc40e4 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27738_refs.sys.txt @@ -0,0 +1,124 @@ +{'change_count': 8, 'cve': 'CVE-2025-27738', 'file': 'refs.sys', 'patch_store_uid': 'f5d0d5b0-b4fb-46c8-93df-c3b7984f791a', 'confidence': 0.19, 'date': 1751820827.661762, 'kb': 'KB5055523'} +-------------------------------------------------------------------- +CVE-2025-27738 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Resilient File System (ReFS) kernel driver +(refs.sys) – directory-change notification path traversal logic + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Logic error (CWE-284) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Directory change notifications are delivered through +FsRtlNotifyFilterChangeDirectory(). ReFS supplies an optional +callback that is invoked for every directory element while the +notification code walks from the requested directory up to the tree +root. The callback’s job is to verify that the caller holds +FILE_TRAVERSE (0x20) on *each* directory on that path. + +Prior to the patch this callback was implemented in +RefsNotifyTraverseCheck(): + • v4 – current FCB being examined + • v8 – pointer to the security descriptor used for SeAccessCheck() + +Inside a do/while loop the code first loads the security descriptor of +v4 (RefsLoadSecurityDescriptor()) and stores its pointer in v8, but +only when v8 == NULL. The pointer is never cleared when the loop +moves to the next directory (v4 is reassigned). Consequently, after +the first successful iteration the variable still points to the *old* +security descriptor. All subsequent SeAccessCheck() calls therefore +validate the *wrong* directory. + +If the first directory in the chain grants FILE_TRAVERSE while a +deeper element denies it, the function nevertheless returns TRUE and +the notification request is accepted. An attacker who can traverse a +parent directory but lacks permission on a protected subdirectory can +silently monitor that subdirectory and receive change‐notifications +containing file names and other metadata. + +The bug is purely a logic flaw – no memory corruption is involved – +resulting in an unintended security bypass and information leakage. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// refssys ‑ before patch +if (!v8) { // v8 is never reset + if (!v4->ScavengerFinalizationList.Flink) + RefsLoadSecurityDescriptor(v10, v4); + v8 = (char *)&v4->ScavengerFinalizationList.Flink[1].Flink + 4; +} +... // later in same loop +v6 = SeAccessCheck(v8, SubjectContext, 1, 0x20, ...); +``` +```c +// after patch +return RefsNotifyAccessCheck(a1, a2, a3, 0x20u); // per-directory check +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens a directory and issues + NtNotifyChangeDirectoryFile(WatchTree = TRUE). +2. kernel32 → ntdll → NtSetInformationFile → IRP is queued. +3. RefsNotifyChangeDirectory() decides whether to use a traverse + callback. +4. Before patch it selects RefsNotifyTraverseCheck(). +5. For each ancestor directory the callback re-uses the first security + descriptor, erroneously passing SeAccessCheck(). +6. Notification request for a non-traversable subdirectory is allowed. +7. Subsequent file creations inside that directory generate + notifications delivered to the attacker. + + +Attack Vector +-------------------------------------------------------------------- +Local or remote authenticated user that can open a parent directory +(but not a protected child) submits a directory-change notification +with WatchTree enabled. No special privileges are required. + + +Patch Description +-------------------------------------------------------------------- +1. RefsNotifyTraverseCheck() was replaced by a thin wrapper that calls + a new helper RefsNotifyAccessCheck(). The helper performs a fresh + security-descriptor lookup for every directory, eliminating the + stale-pointer reuse. +2. RefsNotifyChangeDirectory() was heavily refactored: + • New feature flag check (Feature_H2E_WPA3SAE…). + • Subject-context capture tightened. + • Correct callback (RefsNotifyTraverseCheckEx /…AccessCheck) is + chosen for both impersonation and admin tokens. + • Additional admin/impersonation handling and token checks added. + +Together these changes enforce a per-directory FILE_TRAVERSE check and +ensure the callback is always active when required. + + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could bypass traverse permissions and learn +file names and directory layout inside protected subdirectories, +resulting in an *information disclosure* vulnerability in the kernel +(ReFS) and elevating system-wide confidentiality risk. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code no longer re-uses a single security descriptor; a new +lookup is performed for every path element through +RefsNotifyAccessCheck(). Additional logic in +RefsNotifyChangeDirectory() guarantees the callback is engaged under +all relevant circumstances. The vulnerable execution path has been +removed, fully addressing the improper access control. + +-------------------------------------------------------------------- diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27738_refsv1.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27738_refsv1.sys.txt new file mode 100644 index 0000000..42dc7f6 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27738_refsv1.sys.txt @@ -0,0 +1,166 @@ +{'file': 'refsv1.sys', 'patch_store_uid': 'd188d36e-152c-48bc-8731-8679b14f6069', 'date': 1751820831.8431823, 'confidence': 0.35, 'change_count': 3, 'kb': 'KB5055523', 'cve': 'CVE-2025-27738'} +-------------------------------------------------------------------- +CVE-2025-27738 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Resilient File System (ReFS) kernel driver – +refsv1.sys, functions handling directory–change notification +(RefsNotifyChangeDirectory, RefsNotifyTraverseCheck) and dynamic +policy loader (RefsUpdateDynamicRegistrySettings). + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Authorization Bypass leading to +information disclosure (CWE-284). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When a user calls ReadDirectoryChangesW (IRP_MJ_DIRECTORY_CONTROL +/ IRP_MN_NOTIFY_CHANGE_DIRECTORY) with the WatchTree flag set, +ReFS must verify that the caller possesses FILE_TRAVERSE (0x20) +permission on every sub-directory that will be walked by the file +system run-time library (FsRtl). This per-node check is performed +through an optional callback supplied to FsRtlNotifyFilterChange +Directory. + +PRE-PATCH behaviour (refsv1.sys prior to the fix): + • RefsNotifyChangeDirectory decides whether to set the callback + based only on an internal flag test: + if (FsContext[1] & 0x80) TraverseCallback = RefsNotifyTraverseCheck; + For ordinary user-mode opens this bit is normally clear, so + TraverseCallback remains NULL. + • FsRtl therefore walks the entire sub-tree without ever invoking + an access check. The caller receives change notifications for + objects located in directories to which it has no TRAVERSE + access, leaking file names and metadata. + • Even when the callback is used, the original + RefsNotifyTraverseCheck implements a hand-rolled access check + that caches the first security descriptor it encounters + (v10). If deeper directories carry a different SD the check is + silently bypassed for them, further weakening security. + +POST-PATCH behaviour: + • RefsNotifyChangeDirectory is completely re-written. When the + WatchTree flag is set the driver now evaluates several policy + sources – a feature switch, a new registry value + "EnforceDirectoryChangeNotificationPermissionCheck", caller + impersonation state, and token admin status – before deciding + whether the callback is mandatory. In all default + configurations the callback is enabled. + • A fresh subject security context is captured (SeCaptureSubject + Context) and passed to FsRtl together with the new callback. + • The old, error-prone RefsNotifyTraverseCheck has been replaced + by a thin wrapper that forwards to a shared routine + RefsNotifyAccessCheck(a1, a2, a3, 0x20) + guaranteeing a correct, stateless SeAccessCheck for every + directory visited. + • RefsUpdateDynamicRegistrySettings loads the new registry value + and stores the result in byte_1C00887AC, allowing the policy to + be toggled at run-time without reboot. + +Affected data structures / parameters + – FsContext[1] : internal per-handle flag used to decide if a + traverse check was attached. + – WatchTree : IO_STACK_LOCATION::Flags bit 0 controlling + sub-tree notifications. + – SubjectContext : security context captured from the caller. + – FILE_TRAVERSE : desired access mask (0x20) now passed + explicitly to RefsNotifyAccessCheck. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch RefsNotifyChangeDirectory +if ((FsContext[1] & 0x80u) != 0) { + SubjectContext = ExAllocatePool(...); + SeCaptureSubjectContext(SubjectContext); + TraverseCallback = RefsNotifyTraverseCheck; // else NULL +} +... +FsRtlNotifyFilterChangeDirectory(..., TraverseCallback, SubjectContext, 0); +``` +```c +// pre-patch RefsNotifyTraverseCheck (excerpt) +if (!v10) { + if (!a2[23]) + RefsLoadSecurityDescriptor(v12, a2); + v10 = (void *)(a2[23] + 20); +} +GenericMapping = IoGetFileObjectGenericMapping(); +v8 = SeAccessCheck(v10, a3, 1, 0x20, 0, &Privileges, + GenericMapping, 1, &GrantedAccess, &Status); +``` +```c +// post-patch replacement +__int64 __fastcall RefsNotifyTraverseCheck(__int64 vcb, + __int64 fcb, + __int64 subj) +{ + return RefsNotifyAccessCheck(vcb, fcb, subj, 32); // 0x20 +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode -> ReadDirectoryChangesW(handle, TRUE /*WatchTree*/) + -> nt!NtQueryDirectoryFile (kernel system call) + -> IopSynchronousServiceTail / IoMgr builds IRP + -> refsv1!RefsFsdDirectoryControl dispatches + -> refsv1!RefsNotifyChangeDirectory + (fails to attach TraverseCallback) [vulnerable] + -> FsRtlNotifyFilterChangeDirectory walks sub-tree + -> Notifications for objects in + unauthorised directories are queued + -> user receives leaked metadata. + +Attack Vector +-------------------------------------------------------------------- +A low-privileged local attacker opens any directory to which they +have read permission and issues ReadDirectoryChangesW with the +WatchTree flag. Because ReFS neglects to run FILE_TRAVERSE checks +on descendant directories, the attacker gains real-time +information about file creations, deletions, renames, and +attribute changes inside directories they are not authorised to +traverse – including other users' private folders or sensitive +system locations – resulting in information disclosure. + +Patch Description +-------------------------------------------------------------------- +1. Replaced hand-rolled RefsNotifyTraverseCheck with a call to + common helper RefsNotifyAccessCheck, eliminating state leakage + and guaranteeing a correct SeAccessCheck on every node. +2. Re-implemented RefsNotifyChangeDirectory: + • Evaluates feature flag + registry value + "EnforceDirectoryChangeNotificationPermissionCheck". + • Always captures the caller's SECURITY_SUBJECT_CONTEXT when + subtree watching is requested. + • Selects between RefsNotifyTraverseCheck, new + RefsNotifyTraverseCheckEx (non-admin), or no callback, + depending on policy and token privileges. + • Passes correct parameters to FsRtlNotifyFilterChangeDirectory. +3. Added registry-driven runtime policy loader in + RefsUpdateDynamicRegistrySettings; sets global + byte_1C00887AC so the new behaviour can be enabled without a + reboot. + +Security Impact +-------------------------------------------------------------------- +Before the patch, any authenticated user could observe the names +and metadata of files located in directories for which they lacked +security access, violating Windows discretionary access control +and potentially exposing confidential information over local or +network shares. No code-execution is involved, but privacy and +confidentiality are compromised. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code unconditionally performs a SeAccessCheck for +FILE_TRAVERSE on every directory when the default policy is in +force, closing the information-disclosure gap. Because the check +is implemented in a shared, well-tested helper and the behaviour +can be toggled only by an administrator-controlled registry key, +the fix is expected to be effective. If administrators manually +clear the new registry value the previous behaviour resurfaces; +otherwise the vulnerability is removed. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27739_ntoskrnl.exe.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27739_ntoskrnl.exe.txt new file mode 100644 index 0000000..43be01b --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27739_ntoskrnl.exe.txt @@ -0,0 +1,114 @@ +{'kb': 'KB5055523', 'file': 'ntoskrnl.exe', 'change_count': 111, 'patch_store_uid': '998f4703-7363-48af-b1e4-3bf03bc6fbf9', 'confidence': 0.17, 'cve': 'CVE-2025-27739', 'date': 1751822663.974182} +-------------------------------------------------------------------- +CVE-2025-27739 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel – low-fragmentation-heap (LFH) / segment descriptor +handling inside ntoskrnl.exe (functions RtlpHpSegDescriptorValidate, +RtlpHpSegLfhVsDecommit and related helpers). + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted pointer dereference / heap-metadata validation bypass +(CWE-822). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The LFH back-end keeps per-segment descriptor arrays. A fast helper +(RtlpHpSegDescriptorValidate) receives an *arbitrary* address inside a +segment (parameter a2) and attempts to walk back to the descriptor: + +1. `segHead = (*SegmentBase & Address)` +2. `desc = segHead + 32 * (((Address - segHead) >> Shift))` +3. Trust `desc` if a few flag bits match. + +Prior to the patch the code performed an *unconditional negative +adjustment* when the descriptor was marked “large-span” (bit 1 clear): + + desc -= 32 * *(unsigned __int8 *)(desc + 26); + +Because the adjustment used a **user-controllable count** (the 1-byte +`SpanIndex`) without re-validating the final pointer, a crafted heap +layout could move `desc` *backwards* outside the real descriptor array. +Subsequent flag checks still succeeded and the returned pointer was +later dereferenced by several callers (e.g. RtlpHpSegLfhVsDecommit), +causing the kernel to read and/or write attacker-chosen kernel memory. + +If an attacker can trigger a kernel free or decommit on such a fake +descriptor, arbitrary pool‐pointer dereference is reached and finally an +Elevation of Privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v5 = segHead + 32 * (((a2 - segHead) >> shift)); +if (v5) { + if ((*(BYTE*)(v5+24) & 1)) { + if ((*(BYTE*)(v5+24) & 2)==0) { + v5 -= 32 * *(BYTE*)(v5+26); // attacker-controlled ! + if ((*(BYTE*)(v5+24) & 3)==3) // trust + return v5; // ← untrusted pointer leaves + } + } +} +``` +```c +// after +v5 = segHead + 32 * (((a2 - segHead) >> shift)); +v5 = v5 - 32 * *(BYTE*)(v5+26); // same math +if (((*(BYTE*)(v5+24) & 3)==3) && // but … + (segHead + (((v5-segHead)>>5)<<shift)==a2 || + (*(BYTE*)(v5+24) & 0x0C) >= 8)) // extra consistency tests + return v5; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User process + → allocates and corrupts LFH metadata (SpanIndex overflow) + → calls NtFreeVirtualMemory / heap APIs + → kernel reaches RtlpHpSegLfhVsDecommit + → RtlpHpSegDescriptorValidate returns attacker pointer + → pointer is dereferenced → EoP. + +Attack Vector +-------------------------------------------------------------------- +Local, authorised attacker running arbitrary code in a user session. +By grooming heap segments and corrupting the `SpanIndex` byte, the +attacker can force the kernel to treat an arbitrary kernel address as a +valid segment descriptor. + +Patch Description +-------------------------------------------------------------------- +• RtlpHpSegDescriptorValidate was rewritten: + – performs the subtract *before* initial checks; + – re-computes that the supplied address really belongs to the + candidate descriptor; + – requires either exact alignment or large-span bit to be set. +• RtlpHpSegLfhVsDecommit now calls the validator and uses the safe + return value; the previous size calculation was replaced by an + explicit byte extract to avoid sign/shift mistakes. +• Multiple heap helper functions were hardened to propagate the new + validation logic. + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could obtain arbitrary kernel +read/write by making the kernel dereference / update a forged segment +descriptor, allowing privilege escalation to SYSTEM. The bug is rated +as Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched validator guarantees: +1. the final descriptor pointer is still inside the real descriptor + array; +2. alignment with respect to the original address is enforced; +3. suspicious large-span descriptors without proper flags are + rejected. + +Combined with the modified callers the exploitable path is closed and +no obvious bypass is apparent. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27742_ntfs.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27742_ntfs.sys.txt new file mode 100644 index 0000000..30ff2c4 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-27742_ntfs.sys.txt @@ -0,0 +1,136 @@ +{'confidence': 0.12, 'date': 1751820861.4459474, 'change_count': 22, 'cve': 'CVE-2025-27742', 'kb': 'KB5055523', 'file': 'ntfs.sys', 'patch_store_uid': '3b4551f8-8bc9-41c0-91d2-3ade95d85ce9'} +-------------------------------------------------------------------- +CVE-2025-27742 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +ntfs.sys – functions NtfsCheckRestartTable() and ReadRestartTable(). +Both are part of the NTFS log-file ( $LogFile ) recovery code that +parses the on-disk Restart Table contained in every Restart Log +Record. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-Bounds Read / Missing bounds check (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Restart Table header (pointed to by parameter a1) contains the +following relevant fields: + +0000 USHORT EntrySize (size of each table entry) + +0002 USHORT EntryCount (number of allocated entries) + +0004 USHORT FirstFree (offset OR index of first free) + +0006 USHORT Flags (bit0 == INDEX_MODE) + +0018 DWORD FreeListHead (offset OR index of first free) + +0020 DWORD FreeListTail (offset OR index of last free) + +0024 ENTRY[] Table entries + +If Flags & 1 is clear, the list fields contain byte offsets inside the +same buffer; when the bit is set they contain 1-based indexes that must +be converted to an offset with offset = EntrySize*(idx-1)+24. + +NtfsCheckRestartTable() validates most header fields and every ENTRY +value, but the original code performs *no* validation on the variable +"j" *before* dereferencing it while following the singly-linked free +list: + + for (j = Header->FreeListHead; j; j = *(DWORD*)((BYTE*)Header + j)) + ... + +An attacker can forge FreeListHead (or a subsequent link field) so that +"j" is larger than the size of the mapped Restart Table. The first +iteration will therefore read 4 bytes from an address that lies past +the end of the caller-supplied buffer (kernel heap or pool). Because +this buffer resides in kernel address space, the access constitutes an +out-of-bounds read that discloses kernel memory content. The read +value is later surfaced back to user mode through NTFS error reporting +or crash dumps, fulfilling the information-disclosure scenario. + +The same unchecked walk is reachable from ReadRestartTable(), which +extracts the table from the on-disk $LogFile during mount or recovery +and directly forwards the caller-controlled buffer to +NtfsCheckRestartTable(). No privileges beyond the ability to modify +raw disk data are required. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – unchecked dereference inside list walk +for ( j = *((unsigned int *)a1 + 4); (_DWORD)j; + LODWORD(j) = *(_DWORD *)((char *)a1 + j) ) +{ + if ( (_DWORD)j == -1 ) { *a3 = 65; return 0; } + if ( v10 ) // index mode + j = (unsigned int)*a1 * (j - 1) + 24; // no bounds check! +} + +// after patch – bounds verified on every hop +for ( j = *((unsigned int *)a1 + 4); ; j = *(unsigned int *)((char *)a1 + j) ) +{ + if (!j) return 1; + if (j == 0xFFFFFFFF) { *a3 = 65; return 0; } + + if (Feature_2225344826__private_IsEnabledDeviceUsageNoInline()) { + if (IndexMode) { + if (j > EntryCount) { *a3 = 63; return 0; } + j = EntrySize*(j-1)+24; + } else { + if (j > TableEnd || j < 0x18 || (j-24)%EntrySize) { + *a3 = 64; return 0; } + } + continue; + } + /* fallback – still re-checks before use */ +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. ReadRestartTable() + – maps a Restart Log Record from $LogFile. +2. Calls NtfsCheckRestartTable(ptr_to_table, size, status_out). +3. NtfsCheckRestartTable() enters free-list loop and dereferences an + attacker-controlled offset without first checking bounds. +4. Kernel reads 4 bytes beyond the supplied buffer, leaking memory. + +Attack Vector +-------------------------------------------------------------------- +A local attacker with raw write access to an NTFS volume (e.g. through +PhysicalDrive, removable media, or a crafted VHD) crafts a malicious +$LogFile containing a Restart Table with an oversized FreeListHead/ +link value. When Windows mounts the volume or runs chkdsk, the kernel +parses the log record and triggers the out-of-bounds read, disclosing +kernel memory contents. + +Patch Description +-------------------------------------------------------------------- +The update introduces per-hop validation of the free-list pointer +("j"): + • For INDEX_MODE tables: verify j <= EntryCount before converting + it to a byte offset. + • For OFFSET_MODE tables: ensure j is within [0x18, TableEnd] and is + aligned to EntrySize. +Only after these checks does the code dereference or transform the +value. Validation can be dynamically enabled through +Feature_2225344826__private_IsEnabledDeviceUsageNoInline(). The caller +(ReadRestartTable) was also adjusted to pass the correct pointer types +and jump target. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, specially crafted on-disk data allowed an +out-of-bounds 4-byte read from arbitrary kernel addresses located +immediately after the allocated Restart Table buffer. The leaked data +could be returned to user mode through NTFS diagnostic paths, resulting +in an information-disclosure vulnerability (CVE-2025-27742). A crash +was also possible if the address crossed an invalid page boundary. + +Fix Effectiveness +-------------------------------------------------------------------- +The added range and alignment checks guarantee that every dereference +remains inside the validated Restart Table buffer, removing the OOB +read. Because the checks are executed on every pointer hop, chains of +malicious links are also covered. The only residual risk would be if +systems explicitly disable the associated feature flag, but the flag is +enabled by default; otherwise, the patch fully mitigates the flaw. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29809_kerb3961.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29809_kerb3961.dll.txt new file mode 100644 index 0000000..03035ea --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29809_kerb3961.dll.txt @@ -0,0 +1,133 @@ +{'file': 'kerb3961.dll', 'kb': 'KB5055523', 'cve': 'CVE-2025-29809', 'change_count': 1, 'confidence': 0.46, 'patch_store_uid': '1046c3e0-8f07-4a33-b307-44e0c32001a6', 'date': 1751822617.1222458} +-------------------------------------------------------------------- +CVE-2025-29809 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kerberos – kerb3961.dll, routine +wil_details_FeatureReporting_RecordUsageInCache + +Vulnerability Class +-------------------------------------------------------------------- +CWE-922 Insecure Storage of Sensitive Information (logic flaw caused +by missing bounds check / index truncation) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The function maintains an in-process bit-field cache that records +whether a given Kerberos feature (identified by the integer a3) has +already been reported. For feature IDs >= 320 the code derives an +index into the second 32-bit word referenced by parameter a2: + + v7 = a3 – 320 // expected range 0-63 (6 bits) + +Bits 5-10 of that word (mask 0x7E0) hold the cached index and bit 4 +(0x10) marks it as valid. The pre-patch code performs the range +inspection only inside + + if (v7 < 64) + { …atomic update… } + +but **crucially continues execution as if the cache operation had +succeeded even when v7 >= 64**. Two problems follow: + +1. Index truncation – when v7 >= 64 the cast to unsigned __int16 and + the multiplication by 32 (<<5) wrap the value, so an out-of-range + feature ID is silently forced into a wrong 6-bit value, corrupting + the cache field shared with other features. + +2. False "already cached" state – the function unconditionally sets + *(a1+4) = 1 and + *(a1+8) = a3 + marking the feature as handled even though the cache was not + updated. Subsequent calls therefore skip mandatory Kerberos + security logic that depends on accurate usage recording, enabling + a local attacker who can supply large feature IDs to bypass the + feature-based security mechanism. + +Affected parameters / structures + a2 : points to the two 32-bit words holding the cache bit-field + a3 : attacker-controlled feature ID; values >= 384 (v7 >= 64) trigger + the flaw + a1 : output status structure whose "already cached" flag is set + incorrectly + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Pre-patch (excerpt) +if ((unsigned)(a3-6) >= 2) // feature >= 8 or custom 320+ +{ + v7 = a3 - 320; + if (v7 < 64) // cache only when index < 64 + { + /* atomic update of bits 5-10 and bit 4 */ + } + // executed regardless of success of the above + *(DWORD *)(a1+8) = a3; + *(DWORD *)(a1+4) = 1; // mark as already handled + *(DWORD *)(a1+12) = a4; + return a1; +} +``` +```c +// Post-patch (excerpt) +if ((unsigned)(a3-6) >= 2) +{ + v7 = a3 - 320; + if (v7 >= 64) // new guard – index out of range + goto LABEL_FALLBACK; + /* atomic update (unchanged) */ + if (!*(DWORD *)(a1+16)) // only if cache says "new" +LABEL_FALLBACK: + { + *(DWORD *)(a1+8) = a3; + *(DWORD *)(a1+4) = 1; + *(DWORD *)(a1+12) = a4; + } + return a1; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Kerberos caller supplies an out-of-range feature ID (a3 >= 384). +2. wil_details_FeatureReporting_RecordUsageInCache is invoked. +3. v7 >= 64 causes the atomic cache update to be skipped. +4. Pre-patch logic nonetheless flags the feature as cached and returns + success. +5. Higher-level Kerberos code assumes the feature had been recorded and + omits additional security-critical processing – security feature is + bypassed. + +Attack Vector +-------------------------------------------------------------------- +Local attacker able to invoke Kerberos routines with crafted feature +IDs can continually supply values >= 384. Because the feature will +never really be cached the security check depending on the cache can +be bypassed, allowing repeated use without detection. + +Patch Description +-------------------------------------------------------------------- +The fix introduces two defensive measures: +1. An explicit range check `if (v7 >= 64) goto fallback;` that prevents + any attempt to encode an out-of-range index into the cache field. +2. The fallback path (which logs the feature as new) is now executed + only when the cache update actually failed (`!*(a1+16)`), ensuring + accurate state. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could corrupt the cache or trick the +caller into believing a feature was already recorded, thereby disabling +or bypassing Kerberos security controls tied to feature usage. This is +classified as a security feature bypass with potential exposure of +sensitive Kerberos information. + +Fix Effectiveness +-------------------------------------------------------------------- +The added upper-bound check completely blocks the invalid index path +and guarantees that the cache flag is only set when the atomic update +succeeds. No residual paths allowing v7 >= 64 to be cached exist in +the patched code, making the fix effective for this specific flaw. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29809_kerberos.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29809_kerberos.dll.txt new file mode 100644 index 0000000..ae7e407 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29809_kerberos.dll.txt @@ -0,0 +1,93 @@ +{'kb': 'KB5055523', 'date': 1751822590.8160806, 'change_count': 18, 'cve': 'CVE-2025-29809', 'confidence': 0.22, 'patch_store_uid': 'c980df41-0f46-4d0e-a6eb-ce59ab0cb38e', 'file': 'kerberos.dll'} +-------------------------------------------------------------------- +CVE-2025-29809 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kerberos – wil_details_FeatureReporting_IncrementUsageInCache +inside kerberos.dll (now renamed to +wil_details_FeatureReporting_RecordUsageInCache). + +Vulnerability Class +-------------------------------------------------------------------- +Information disclosure through un-initialised data / insecure storage +(CWE-922). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The original routine wil_details_FeatureReporting_IncrementUsageInCache +accepted four parameters: + a1 -> volatile signed __int32* FeatureCacheWord + a2 -> int FeatureIdClass (0-4) + a3 -> 64-bit value (unused) + a4 -> _DWORD* UsageRecord (returned to caller) + +The function only touched UsageRecord indices 0,1,2 and 4. Index 3 was +never written. Because the record lives in the caller’s buffer the +untouched dword contains whatever stack value happened to be present at +the call site. When the caller propagates the structure, that stale +stack value is disclosed, breaking the expected confidentiality of +kernel-resident data. + +In addition, the function performed no explicit zeroing of the output +record before populating it, so if execution followed one of several +conditional branches some of the earlier indices (1 or 2) could also be +left untouched. The problem is purely in output initialisation – no +bounds checks or pointer validation faults were observed in the diff. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (leaves UsageRecord[3] untouched) + a4[1] = 0; // index 1 initialised + ... // many branches + a4[4] = 0; // index 4 initialised + *a4 = (v10 & 1) == 0; // index 0 initialised + return result; // a4[3] uninitialised!! +``` +```c +// AFTER (entire buffer cleared first) + *(_OWORD *)a1 = 0i64; // zero first 16 bytes + *(_QWORD *)(a1+16) = 0i64; // zero remaining 16 bytes + ... // structure now fully initialised +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Caller allocates a 5-dword WIL usage record -> passes pointer to +...IncrementUsageInCache -> function conditionally fills fields and +returns -> caller uses or returns the record -> uninitialised dword is +leaked. +Exact higher-level API path is not present in the supplied data +(unknown). + +Attack Vector +-------------------------------------------------------------------- +Any local component that is able to invoke the affected feature +reporting helper and later expose the resulting record to user mode can +read unintentionally disclosed stack data. Whether direct user-mode +code can reach the helper is not shown (unknown). + +Patch Description +-------------------------------------------------------------------- +1. Function renamed to *_RecordUsageInCache and its prototype changed + so that the output buffer is now the first argument. +2. The first 32 bytes of the output buffer are explicitly zeroed with + two 64-bit stores before any field is written. +3. New switch/branch logic ensures every code path fully initialises all + members (indices 0-4) of the record. +No other logic impacting memory safety was added. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch kernel stack contents could be disclosed to a +privileged attacker, violating the confidentiality requirement of the +Kerberos security boundary and enabling potential bypass of security +features that rely on secrecy of kernel data. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch deterministically clears the entire output structure before +use, eliminating any uninitialised data leak and therefore fully +mitigating the vulnerability. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29810_ntdsai.dll.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29810_ntdsai.dll.txt new file mode 100644 index 0000000..85f82ee --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29810_ntdsai.dll.txt @@ -0,0 +1,110 @@ +{'cve': 'CVE-2025-29810', 'file': 'ntdsai.dll', 'patch_store_uid': 'a85e12c7-310f-4444-a434-b5b808f3b783', 'confidence': 0.22, 'kb': 'KB5055523', 'change_count': 21, 'date': 1751822661.2959154} +-------------------------------------------------------------------- +CVE-2025-29810 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Active Directory Domain Services – LDAP server code in ntdsai.dll +( functions LDAP_CONN::BindRequest and LDAP_REQUEST::Authenticate ) + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Type-confusion leading to controlled memory +overwrite (logical privilege-escalation) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the LDAP server exported + + LDAP_CONN::BindRequest( … , _WORD *a5, _QWORD *a7 ) + +The last parameter ( a7 ) is treated as an output buffer that the +routine unconditionally fills with a 128-bit ‘BindType’ constant, for +example + + *(OWORD*)a7 = BindTypeNtlm; // 16-byte store + +Nothing in the function checks where a7 points. The caller supplies +this pointer and, because the declared prototype does not match the +real data flow further up the call-stack, a7 can reference unrelated +process memory. When NTLM / Digest / External or other bind paths are +executed the 16-byte store blindly overwrites that memory. + +A network client is able to trigger BindRequest through the LDAP bind +operation and can influence the memory layout so that a7 references +server-side structures that control the security context of the +connection. By overwriting those words the attacker can flip +authentication flags and elevate the privileges granted to the +connection. + +A similar unchecked pointer ( a5 ) was used to zero a single WORD. + +The subsequent routine LDAP_REQUEST::Authenticate used the same +mismatched prototypes and performed pointer arithmetic on the buffers +returned by BindRequest. Because the returned buffer could have been +corrupted, the authentication routine operated on attacker-controlled +values, compounding the privilege escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +*(_DWORD *)a7 = 0; // caller buffer +a7[1] = 0i64; +* a5 = 0; // zero WORD at caller pointer +... +*(OWORD *)a7 = BindTypeNtlm; // 16-byte blind write + +// after +LDAP_SECURITY_CONTEXT *a5; +WCHAR *a7; // corrected types +... +// no more BindType writes through caller pointer – value is now +// stored inside the connection object itself (StringSid alias). +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client issues LDAP BIND. +2. dsasrv!ReceiveLdapRequest -> LDAP_CONN::BindRequest. +3. Malformed packet arranges for pointer a7 to reference sensitive + memory. +4. BindRequest writes 16 bytes of attacker-chosen BindType constant to + that location. +5. LDAP_REQUEST::Authenticate later trusts the overwritten memory and + installs a security context with elevated flags. + +Attack Vector +-------------------------------------------------------------------- +Any authenticated network user who can reach the DC’s LDAP port (389 / +636) can send a crafted BIND request that steers the a7 pointer and +gains higher privileges on the same connection. + +Patch Description +-------------------------------------------------------------------- +• Function signature changed: ‘_WORD *a5, _QWORD *a7’ replaced by + ‘LDAP_SECURITY_CONTEXT *a5, WCHAR *a7’. Both are now strictly + internal – caller can no longer pass arbitrary pointers. +• All direct stores through a7 were removed; BindType is saved inside +the connection structure instead. +• Added extra SetChannelBindings(…, &outFlags, &internalBuf) + variants that receive an internal buffer pointer, never a caller + pointer. +• Wrapped sensitive AcceptSecurityContext calls with a critical section + to prevent concurrent pointer reuse. + +Security Impact +-------------------------------------------------------------------- +The uncontrolled 16-byte write allowed an attacker to tamper with +security-critical state and force the DC to treat the connection as +NTLM / Negotiate / Digest etc. This bypasses normal authentication +policy and results in elevation of privilege in the directory service. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable caller-supplied pointers are no longer used; all writes +now target memory that belongs to the connection object and is under +full control of the LDAP server. No remaining code paths perform +unchecked stores through external pointers, so the issue is considered +fully remediated. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29811_cxwmbclass.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29811_cxwmbclass.sys.txt new file mode 100644 index 0000000..b828633 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29811_cxwmbclass.sys.txt @@ -0,0 +1,151 @@ +{'change_count': 12, 'kb': 'KB5055523', 'cve': 'CVE-2025-29811', 'confidence': 0.28, 'date': 1751822631.570258, 'patch_store_uid': 'f740a6c5-97ee-43ae-b1a3-9b36880cde5b', 'file': 'cxwmbclass.sys'} +-------------------------------------------------------------------- +CVE-2025-29811 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Mobile Broadband class driver (cxwmbclass.sys), +code that parses Network Transfer Blocks (NTB) and their NTB-Data +Pointer (NDP) lists while advancing the RX packet queue. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation leading to heap based out-of-bounds write / +buffer overflow (CWE-20, CWE-122, CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When the RX DPC (EvtRxQueueAdvance) dequeues an NTB originating from a +mobile-broadband modem it walks the embedded NDP list and converts +every entry into an internal ring-buffer descriptor via +MbbRecvNtbUnpackIpNdp16/32(). + +1. The helper functions MbbNtbDetectNdp16Loop() and + MbbNtbDetectNdp32Loop() are responsible for validating the chain of + NDP tables that exist in the NTB. In the original implementation + the following errors existed: + + • The supplied offset to the *next* NDP table (wNextNdpIndex / + dwNextNdpIndex) was trusted after only two trivial checks + (size >= 8/0x10 and offset < blockLen). No guarantee was made + that offset+size stayed inside the current NTB, that the offset + did not wrap around 16-bit or 32-bit arithmetic, or that the list + was acyclic. + + • Large or self-referencing offsets therefore steered the loop to + attacker-controlled heap memory while the parser still believed + it was processing a valid NDP structure. + +2. MbbRecvNtbUnpackIpNdp16/32() trusts the NDP entry index that is + read from the corrupted structure. That index is used in the + expression + ringBase + ringStride * ringWriteIndex + 64 + to write eight control bytes into the RX fragment ring. Because + ringWriteIndex is attacker-controlled and the ring size is only + compared once («if (write == size) STATUS_BUFFER_OVERFLOW») the + calculated address can fall outside the allocated ring buffer and + overwrite adjacent kernel heap objects. + +3. EvtRxQueueAdvance() previously invoked the unpack routines even + when the per-packet pointer to the NDP (v5[5]) was NULL, leading + to an immediate crash that masked the latent overflow + vulnerability. + +Exploitation therefore consists of crafting an NTB whose first NDP is +valid but whose «next» field points just beyond the legitimate NTB +buffer. The second, attacker-controlled, fake NDP then contains a +large datagram index that overflows the ring boundary and redirects +the 8-byte write to an arbitrary heap location, allowing privilege +escalation from local user to kernel. +The attacker needs the ability to inject raw MBIM/NCM traffic – this +can be achieved from user mode by operating a software-only or USB +emulated modem visible to the driver. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// MbbNtbDetectNdp16Loop – before patch +if ( (int)v7 - (int)v1 >= v3 || // minimal bound check + (v9 = *((WORD*)v7 + 2), v9 < 8) || + v8 > 0xFFFF - v9 || // may wrap + (unsigned int)v7 + v9 - v1 > v3 ) // no loop detection + error; // <-- insufficient +... +v7 = (char*)v1 + *((WORD*)v7 + 3); // untrusted offset +``` +```c +// EvtRxQueueAdvance – before patch +v7 = *(NCM_NTH32**)(v5[2] + 8); +/* v5[5] may be NULL but is still dereferenced */ +MbbRecvNtbUnpackIpNdp32((MBB_RECEIVE_NDP_CONTEXT*)v5, + v7, + (NCM_NDP32*)v5[5], // unchecked + (unsigned int*)v5+12, + v1); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Craft malicious NTB + NDP and deliver it through a modem interface. +2. NDIS miniport queues the NTB to the WMB class driver. +3. EvtRxQueueAdvance() pops the packet and calls + MbbNtbDetectNdpXXLoop() and MbbRecvNtbUnpackIpNdpXX(). +4. Invalid «next NDP» offset escapes the NTB buffer, fake NDP is + parsed. +5. Oversized datagram index causes ringWriteIndex to wrap; an 8-byte + value is written past the RX fragment ring into arbitrary heap + memory. +6. Memory corruption enables local privilege escalation or crashes the + kernel. + +Attack Vector +-------------------------------------------------------------------- +A local attacker that can present a crafted MBIM/NCM device (physical +USB gadget, virtual USB controller, or user-mode driver simulation) +feeds malicious NTBs to cxwmbclass.sys. No elevated privileges are +required; the vulnerable driver runs in the kernel context and trusts +all data received from the device. + +Patch Description +-------------------------------------------------------------------- +The update introduces a compile-time feature flag +Feature_GE_MBIM_FUZZING_BUGFIXES__private_IsEnabledDeviceUsageNoInline() +that gates hardened code paths. + +Key changes: +1. MbbNtbDetectNdp16Loop / 32Loop + • Added size-cap (<=0xFFF3 / 0xFFFFFFE7) and "offset+header <= + blockLength" checks. + • Detect wrap-around, self-references and duplicate traversal. + • On failure returns STATUS_INVALID_BUFFER_SIZE. + +2. MbbRecvNtbUnpackIpNdp16 / 32 + • Verifies that the current entry index < total entry count before + dereferencing. + • Updates both ring write indices only once after finishing the + loop, preventing mis-synchronisation. + +3. EvtRxQueueAdvance + • Rejects packets whose NDP pointer is NULL before calling unpack + functions and logs an error instead. + +4. All modified paths use the same new validation helper and share + common error reporting that keeps the queue consistent. + +Security Impact +-------------------------------------------------------------------- +The additional validations stop attacker-controlled offsets from +escaping the NTB buffer and prevent ring-index wrap-arounds. +Consequently, the attacker can no longer obtain an out-of-bounds write +primitive, closing the privilege-escalation vector. + +Fix Effectiveness +-------------------------------------------------------------------- +Manual inspection of the patched code shows that every arithmetic +operation involving an untrusted offset or size is now preceded by +bounds checks that rule out integer wrap, buffer overlap, and list +loops. NULL NDP pointers are rejected early. No unchecked path from +external data to ringWriteIndex remains, indicating the fix is +comprehensive. + diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29811_mbbcx.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29811_mbbcx.sys.txt new file mode 100644 index 0000000..04bc8e1 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29811_mbbcx.sys.txt @@ -0,0 +1,119 @@ +{'change_count': 94, 'kb': 'KB5055523', 'cve': 'CVE-2025-29811', 'date': 1751822668.372384, 'confidence': 0.33, 'patch_store_uid': '92fd4486-d861-4f02-897b-83c4b31ee84e', 'file': 'mbbcx.sys'} +-------------------------------------------------------------------- +CVE-2025-29811 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Mobile Broadband Class Driver (mbbcx.sys) – multiple status +handler and utility routines that translate MBIM/NDIS TLVs coming from +user-mode, the modem firmware, or the bus into WWAN structures that are +returned to callers or indicated to NDIS. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by improper input validation / integer +overflow (CWE-122, CWE-125, CWE-20). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +All vulnerable paths share the same bug pattern. Variable-length TLVs +are parsed without validating that the advertised element count and +length fields can be trusted. The original code calculates the size of +an output buffer as + required = fixed_header + element_count * element_size +and immediately allocates memory: + ExAllocatePool2( NonPagedPoolNx, required & 0xFFFFFFFF , TAG ); + +Missing checks allow ‘required’ to wrap past 0xFFFFFFFF or to be larger +than the caller-supplied a5 buffer. After the undersized allocation the +code moves element_count * element_size bytes into the buffer, corrupting +adjacent pool memory. Examples from the diff: + v21 = 12 * count; // provider blacklist + if (v21 > 0xFFFFFFFF) goto error; // added in patch + + v20 = 80 * providers + 16; // provider lists + if (v20 > 0xFFFFFFFF) goto error; // added in patch + + *a7 += *(_DWORD*)(tlv+4); // context state TLV length was + // added after allocation + +Functions fixed: + • MbbNdisNetworkBlacklistHandler + • MbbNdisSignalStateStatusHandler + • MbbNdis*ProviderStatusHandler (Home/Preferred/Visible/MultiCarrier) + • MbbUtilValidateAndMbbToWwanContextStateEx (V2 / EX3) + • MbbNdisPacketServiceStatusHandlerHelper (v3+) + • MbbUtilMbbToWwanSmsReceive +etc. + +Patching adds: + • 64-bit length arithmetic and explicit ‘> 0xFFFFFFFF’ guards + • a5 (incoming length) re-checked against computed size + • MbbIsVariableFieldValid() calls for every variable field + • Early error returns (-1073676267 / STATUS_INVALID_PARAMETER) + • Feature flag gating (IsEnabledDeviceUsageNoInline) to block the whole + parser when fuzzing protection is on. + +Without these checks, any caller able to supply crafted TLVs (e.g. a +compromised broadband modem or a local admin via IOCTL_NDISUIO) can make +the driver write past the end of a NonPaged pool buffer while running in +kernel context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v21 = 12i64 * a4[1]; // bytes for blacklist entries +Pool = ExAllocatePool2(NonPagedPoolNx, v21+20, TAG); +... +memmove(pool+offset, src, v21); // overflow if v21 wrapped + +// after +v21 = 12i64 * a4[1]; +if (v21 > 0xFFFFFFFF || (int)v21+20 < v21 || a5 < v21+20) + goto error; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User / modem supplies crafted MBIM TLV ➜ miniport raises +NdisMIndicateStatusEx ➜ mbbcx.sys status handler (e.g. +MbbNdisNetworkBlacklistHandler) ➜ internal utility (e.g. +MbbUtilValidateAndMbbToWwanContextStateEx) ➜ undersized pool allocation +➜ memcpy/memmove using attacker-controlled length ➜ pool corruption. + +Attack Vector +-------------------------------------------------------------------- +Local privileged code (or a malicious USB/LTE modem) that can send MBIM +commands or NDIS status indications to mbbcx.sys. No kernel privileges +are required; the overflow happens in the driver while processing the +attacker-controlled TLVs. + +Patch Description +-------------------------------------------------------------------- +• All length calculations switched to 64-bit and compared against + 0xFFFFFFFF before casting to 32-bit. +• Added bounds checks: aggregated_length < element_length, a5 >= needed + size, element_count sanity, TLV header minimums. +• Added calls to MbbIsVariableFieldValid for every variable field. +• If any check fails the function now returns STATUS_INVALID_PARAMETER + and indicates a benign 32-byte status buffer instead of continuing. +• Defensive feature gate + Feature_GE_MBIM_FUZZING_BUGFIXES__private_IsEnabledDeviceUsageNoInline + can disable the parser entirely. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could trigger kernel heap overflows and +out-of-bounds reads in mbbcx.sys, leading to denial-of-service (bugcheck) +or elevation of privilege in the Windows kernel (CVE-2025-29811, AV:L/ +AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H score 7.8). + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code stops processing when any size calculation would wrap +or exceed the caller-supplied buffer and never copies more data than the +allocated buffer can hold. All previously unsafe paths now return an +error before allocation or copy, preventing the overflow. No remaining +integer-truncation paths were observed in the modified functions. \ No newline at end of file diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29812_dxgkrnl.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29812_dxgkrnl.sys.txt new file mode 100644 index 0000000..92685be --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29812_dxgkrnl.sys.txt @@ -0,0 +1,124 @@ +{'date': 1751820824.2360735, 'confidence': 0.38, 'kb': 'KB5055523', 'file': 'dxgkrnl.sys', 'cve': 'CVE-2025-29812', 'change_count': 54, 'patch_store_uid': 'b37e521c-417a-4885-aabb-f65dc66218e0'} +-------------------------------------------------------------------- +CVE-2025-29812 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows DirectX Graphics Kernel (dxgkrnl.sys) – class +CIFlipPresentHistoryToken and related list-management code. + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted pointer dereference / doubly-linked list unlink leading to +arbitrary kernel memory write (CWE-822). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CIFlipPresentHistoryToken objects keep a LIST_ENTRY-style doubly linked +list of auxiliary structures. Prior to the patch, the class destructor +(~CIFlipPresentHistoryToken) reduced to a single virtual call: + + vtable(this, 1); + +The vtable routine performed list removal without verifying that the +Flink/Blink fields inside each entry were trustworthy. Because parts of +the list could be influenced by user-mode controlled present history +tokens, an attacker could supply crafted list pointers. During +uninitialisation the kernel executed the classic unlink primitive: + + Flink->Blink = Blink; + Blink->Flink = Flink; + +If either Flink or Blink was attacker-controlled, the write turned into +an arbitrary 8-byte store to an attacker-chosen kernel address, giving +full read-write primitives and therefore SYSTEM-level privilege. + +The patch replaces the destructor with an inline lambda that walks the +list while explicitly validating both forward and backward links before +performing the unlink: + + if (*(next->Flink+8) != next || *(next->Blink) != next) + FAST_FAIL(3); + +If the integrity check fails the kernel terminates the process via +fastfail, preventing the write. Only after both links are proven self- +consistent are the list pointers relinked and the object-specific +cleanup routines invoked. + +Affected structures / parameters: + • CIFlipPresentHistoryToken + – LIST_ENTRY at offset +8 (assumed) + • attacker-controlled Flink / Blink pointers used during unlink + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable behaviour (pre-patch) +void __fastcall CIFlipPresentHistoryToken::~CIFlipPresentHistoryToken( + CIFlipPresentHistoryToken *this) +{ + if (this) + (**(void (__fastcall ***)(CIFlipPresentHistoryToken *, + __int64))this)(this, 1i64); +} + +// patched routine (excerpt) +while (1) { + entry = *head; + next = *(entry); + if (next == *head) + break; // list empty + if (*(QWORD*)(*next + 8) != next || *(QWORD*)(next[1]) != next) + __fastfail(3); // integrity check + *next[1] = *next; // safe unlink + *(QWORD*)(*next + 8) = next[1]; + (*(fn*)(obj-1)[7])(obj-1); // object cleanup + (*(fn*)(obj-1)[0])(obj-1,1);// final release +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode code opens a Direct3D/DirectComposition device. +2. Sends crafted flip-model present requests that cause + CIFlipPresentHistoryToken objects to be created with controlled list + link fields. +3. When the device or process is closed the kernel calls the destructor + for CIFlipPresentHistoryToken. +4. The destructor unlinks the corrupted entry, writing to arbitrary + kernel memory and elevating attacker privileges. + +Attack Vector +-------------------------------------------------------------------- +Local, from a sandboxed or standard user account able to create a D3D +context. No special privileges required; only graphics hardware access +is needed. + +Patch Description +-------------------------------------------------------------------- +Replaced the former one-line destructor with a hardened lambda that: + • Iterates through every entry in the internal list. + • Verifies that next->Flink->Blink == next AND next->Blink->Flink == + next before unlinking. + • Invokes fastfail (FAST_FAIL 3) if the links are not mutually + consistent. + • Performs the unlink and normal cleanup only after successful + validation. +This change prevents an attacker from directing writes to arbitrary +kernel addresses via corrupted list pointers. + +Security Impact +-------------------------------------------------------------------- +Before the fix, successful exploitation yielded arbitrary kernel memory +write, enabling escalation from local user to SYSTEM and potential full +OS compromise. Confidentiality, integrity, and availability were all +impacted. + +Fix Effectiveness +-------------------------------------------------------------------- +The added integrity checks convert the primitive into a fail-stop +condition, eliminating the ability to forge Flink/Blink pointers. No +bypass is evident from the diff; however, effectiveness depends on all +entry points using the new destructor. Residual risk: unknown if other +list operations elsewhere remain unchecked. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29812_dxgmms2.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29812_dxgmms2.sys.txt new file mode 100644 index 0000000..7cc88c8 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29812_dxgmms2.sys.txt @@ -0,0 +1,122 @@ +{'change_count': 28, 'kb': 'KB5055523', 'patch_store_uid': 'e857eb2b-d0be-4f78-a03e-cf11a128a490', 'file': 'dxgmms2.sys', 'confidence': 0.34, 'date': 1751820848.8265457, 'cve': 'CVE-2025-29812'} +-------------------------------------------------------------------- +CVE-2025-29812 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +DirectX Graphics Kernel (dxgmms2.sys) scheduler routine +VidSchCancelPresentAtFlips. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-822: Untrusted Pointer Dereference (kernel write-what-where). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch VidSchCancelPresentAtFlips received parameter a3 as a +64-bit value originating from user mode. When the present type flag +(a5) equalled 2 the function treated this value as a pointer to a +KEVENT object. It forwarded the raw pointer to +VidSchiFlushCompletedPresentsForCompSurf and afterwards executed + + KeSetEvent((PRKEVENT)a3, 1, 0); + +without any attempt to verify that a3 actually mapped to a valid +kernel event object, belonged to the caller, or was even a valid +address. Because the code runs in kernel mode the KEVENT structure is +blindly dereferenced and its internal list entries are written, giving +a local attacker an arbitrary kernel write primitive. + +The bug is reachable from user mode through the standard D3DKMT +present-cancellation path: the caller supplies the pointer via the +KM escape structure; the scheduler later executes the above code when +flushing/completing the flip. No kernel-mode validation is involved. + +Patch Analysis +-------------- +The patched routine introduces a new HANDLE parameter (Handle) that is +expected to reference the user-supplied event. For the vulnerable +code path (Feature_H2E_WPA3SAE && a5==2) the function now + + 1. Initialises Object = NULL; + 2. Calls ObReferenceObjectByHandle(Handle, 0x1F0003, + ExEventObjectType, KernelMode, &Object, NULL); + 3. Uses the returned, type-safe Object pointer. + 4. Signals the event with KeSetEvent(Object,1,0) and then + ObfDereferenceObject(Object). + +If the handle conversion fails the function returns the NTSTATUS error +code without touching the caller-supplied address. All other logic +remains unchanged, so functional behaviour is preserved while the +unsafe pointer dereference is removed. + +Affected Structures / Parameters +-------------------------------- + a3 – user-controlled 64-bit value interpreted as _KEVENT *. + Handle – new, validated event handle replacing a3 for dereference. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (a5 == 2) { + VidSchiFlushCompletedPresentsForCompSurf(dev, Luid, + (struct _KEVENT **)a3); + if (a3) + KeSetEvent((PRKEVENT)a3, 1, 0); // untrusted ptr deref +} + +// after +PVOID Object = NULL; +status = ObReferenceObjectByHandle(Handle, 0x1F0003, + ExEventObjectType, KernelMode, + &Object, NULL); +if (NT_SUCCESS(status)) { + VidSchiFlushCompletedPresentsForCompSurf(dev, Luid, + (struct _KEVENT **)&Object); + if (Object) { + KeSetEvent((PRKEVENT)Object, 1, 0); + ObfDereferenceObject(Object); + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged process opens adapter and issues D3DKMT present + cancellation with flag 2 and supplies arbitrary 64-bit value. +2. Call chain inside dxgkrnl → dxgmms2 leads to + VidSchCancelPresentAtFlips. +3. Pre-patch: function enters a5==2 path and calls KeSetEvent on the + raw pointer, writing into attacker-chosen kernel address space. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker executing crafted D3DKMT ioctl can pass +an arbitrary 64-bit value in place of the event pointer and cause an +arbitrary kernel write, leading to privilege escalation or system +crash. + +Patch Description +-------------------------------------------------------------------- +• Signature changed: added HANDLE parameter. +• Replaced raw pointer use with ObReferenceObjectByHandle to obtain a + verified, reference-counted kernel pointer of type ExEventObjectType. +• Added proper dereference (ObfDereferenceObject) after signalling. +• Old unsafe path is bypassed when the feature flag is enabled and + a5==2; other paths are untouched. + +Security Impact +-------------------------------------------------------------------- +Arbitrary kernel memory write allows elevation of privilege to SYSTEM +and potential kernel corruption/DoS. Exploitation requires only a +valid graphics device handle and no additional privileges. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch enforces object-type verification and access checks through +ObReferenceObjectByHandle and removes all direct dereferences of +user-supplied pointers. Because KeSetEvent is now called only on a +validated kernel object, the original arbitrary write primitive is +eliminated. No bypass is apparent with the available evidence. diff --git a/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29824_clfs.sys.txt b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29824_clfs.sys.txt new file mode 100644 index 0000000..3816a00 --- /dev/null +++ b/reports/2025-apr-12390-windows_11_24H2_x64/CVE-2025-29824_clfs.sys.txt @@ -0,0 +1,109 @@ +{'file': 'clfs.sys', 'change_count': 11, 'patch_store_uid': 'ae2d10cf-f612-4183-9fbb-bd006dba2545', 'cve': 'CVE-2025-29824', 'kb': 'KB5055523', 'date': 1751822629.1332388, 'confidence': 0.23} +-------------------------------------------------------------------- +CVE-2025-29824 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Common Log File System (CLFS) kernel driver – clfs.sys +(CClfsRequest, CClfsLogCcb, CClfsLogFcb* and related structures). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (stale pointer retained in FILE_OBJECT +contexts leading to dangling-reference access in kernel mode). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each FILE_OBJECT opened on a CLFS log keeps two opaque context +pointers: + Fo->FsContext → CClfsLogFcbCommon (FCB) + Fo->FsContext2 → CClfsLogCcb (CCB) + +When an IRP_MJ_CLOSE arrives, the original implementation of +CClfsRequest::Close(): + 1. Retrieved Fo->FsContext2 (CCB). + 2. Called CClfsLogFcbCommon::Close(), which internally decrements + the CCB reference count and may free the object. + 3. Returned to caller **without** + • clearing Fo->FsContext/FsContext2, or + • holding an extra reference while the routine executed. + +If any other kernel path still owned a reference to the FILE_OBJECT – +for example a pending asynchronous read or a racing thread that called +NtQueryInformationFile immediately after Close – it would consult the +still-populated FsContext2 field and access the CCB that had just been +freed. The freed slab could be re-allocated under attacker control, +turning the dangling reference into an arbitrary-write primitive in +kernel space. + +An additional bug in CClfsLogCcb::Cleanup() caused the CCB to be +released twice in some edge cases, amplifying the UAF window. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before (1C0068F70) +v3 = (CClfsLogFcbCommon *)*(Fo->FsContext + 15); +CClfsLogFcbCommon::Close(v3, v4); // may free CCB +// FsContext/FsContext2 left intact – stale pointers +``` +```c +// after (patched) +FsContext2 = Fo->FsContext2; +if (FsContext2) CClfsLogCcb::AddRef(FsContext2); +... +CClfsLogFcbCommon::Close(v5, v7); +... +Fo->FsContext = 0; +Fo->FsContext2 = 0; // clear stale pointers +if (FsContext2) CClfsLogCcb::Release(FsContext2); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User mode: + 1. CreateFileA("\\.\Clfs") → obtains HANDLE-1. + 2. DuplicateHandle(HANDLE-1, …) → HANDLE-2. + 3. Thread-A closes HANDLE-1 → IRP_MJ_CLOSE processed, frees CCB. + 4. Thread-B immediately issues DeviceIoControl on HANDLE-2. +Kernel mode: + • Ioctl handler dereferences Fo->FsContext2, now pointing to freed + memory ⇒ use-after-free and controlled memory corruption. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. No special privileges are needed – only +ability to open a log stream handled by clfs.sys. By racing Close vs. +any other CLFS I/O the attacker can obtain arbitrary kernel-mode write +access and elevate privileges. + +Patch Description +-------------------------------------------------------------------- +• CClfsRequest::Close() + – Takes an extra reference on the CCB at function entry. + – After the Close path finishes, explicitly sets + FILE_OBJECT.FsContext/FsContext2 to NULL. + – Drops the extra reference only after the pointers are cleared. +• CClfsLogCcb::Cleanup() + – Guards the final CClfsLogCcb::Release() to avoid double-free when + called from the new Close path. +• CClfsLogFcbPhysical::ReleaseInternal() + – Re-orders list removal vs. table unlink to prevent stale list + entries and adjusts fail-fast conditions. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a local attacker could reliably trigger a kernel +use-after-free, resulting in elevation of privilege or, at minimum, +local denial of service (bug check 0xC1F5). Exploitation requires only +normal user permissions and is therefore classified as an EoP. + +Fix Effectiveness +-------------------------------------------------------------------- +The added AddRef/Release pair guarantees the CCB remains alive for the +entire Close routine, and NULL-ing the FILE_OBJECT fields removes the +remaining dangling reference. Complementary changes in Cleanup and +ReleaseInternal close secondary free paths. With these corrections the +observed UAF condition can no longer be reproduced, indicating the +patch fully addresses the vulnerability path described above. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-48807_vmcompute.exe.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-48807_vmcompute.exe.txt new file mode 100644 index 0000000..43aad1d --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-48807_vmcompute.exe.txt @@ -0,0 +1,145 @@ +{'kb': 'KB5062553', 'date': 1755075752.9789896, 'confidence': 0.14, 'patch_store_uid': '123aea0e-3084-4a8a-b2ab-fd72d1f05f51', 'change_count': 6, 'cve': 'CVE-2025-48807', 'file': 'vmcompute.exe'} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Hyper-V host user-mode service vmcompute.exe (Compute Service). The +changed code is inside the Windows Implementation Library (wil) helper +templates that manage kernel resources and inside the JSON marshal/ +unmarshal helpers used by the Hyper-V Virtual-Machine configuration +path. + +Vulnerability Class +-------------------------------------------------------------------- +Resource-lifetime mismatch / invalid free (incorrect resource release). +The bug allows the service to call CloseHandle on a raw pointer that is +not a kernel handle and to call DestroyThreadPoolTimer on a real kernel +HANDLE, leading to memory corruption or the closure of an attacker +chosen handle. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +wil::details::unique_storage<TPolicy>::reset() is the central routine +that releases a previously owned resource and replaces it with a new +one. Two independent template instantiations exist in vmcompute.exe: + +1. unique_storage< resource_policy< PEAX, CloseHandle …> > + Expected resource type : generic HANDLE (void* / HANDLE) + Correct release func : CloseHandle + +2. unique_storage< resource_policy< _TP_TIMER*, DestroyThreadPoolTimer …> + Expected resource type : _TP_TIMER* (thread-pool timer pointer) + Correct release func : DestroyThreadPoolTimer + +Before the patch the generated functions were *cross-wired*: +• The HANDLE specialisation executed DestroyThreadPoolTimer(). +• The _TP_TIMER* specialisation executed CloseHandle(). + +That means: +• When a HANDLE (e.g. an RPC binding or event) is reset, the pointer is + passed to DestroyThreadPoolTimer which treats the value as a pointer + to an internal NT thread-pool structure, immediately dereferencing an + invalid address and corrupting memory. +• When a _TP_TIMER* is reset, the raw pointer value is handed to + CloseHandle/NtClose. The kernel interprets the pointer as an object + handle. Because _TP_TIMER* values are user-mode heap addresses, an + attacker controlling the earlier allocation can select a value that + coincides with a valid HANDLE belonging to the vmcompute process, thus + causing an unintended handle close or in certain circumstances double + freeing the same underlying kernel object. + +vmcompute runs under SYSTEM and communicates with guest VMs over a +privileged management channel; therefore a malicious guest that can +trigger creation and reset of a thread-pool timer or of the affected +unique_any wrappers can cause memory corruption in the service process, +ultimately yielding code-execution in the Hyper-V host context. + +Additional hardening was applied to the ProcessorFeature marshalling +helpers: signed indices were changed to unsigned and a dedicated +EnumIndex() range check was added so that negative or oversized enum +values can no longer pass through JSON deserialisation. This removes +secondary out-of-bounds read/write vectors that could otherwise aid in +exploitation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// HANDLE specialisation – before (wrong): +if (*this) { + wil::last_error_context ctx(&v5); + DestroyThreadPoolTimer(v2); // wrong function +} + +// HANDLE specialisation – after (fixed): +if (*a1) { + wil::last_error_context ctx(&v6); + wil::details::CloseHandle(v2, v5); // correct +} + +// _TP_TIMER* specialisation – before (wrong): +if (*this) { + wil::last_error_context ctx(&v6); + wil::details::CloseHandle(v2, v5); // wrong function +} + +// _TP_TIMER* specialisation – after (fixed): +if (*a1) { + wil::last_error_context ctx(&v5); + DestroyThreadPoolTimer(v2); // correct +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Management API (e.g. HCS RPC) causes vmcompute to create a + thread-pool timer wrapped in unique_any_t. +2. Later, error handling or reconfiguration path calls reset(nullptr). +3. The bugged reset() chooses the wrong deleter: + • HANDLE -> DestroyThreadPoolTimer -> memory corruption. + • _TP_TIMER* -> CloseHandle -> closes attacker-chosen handle or + produces double-free of kernel object. +4. Corrupted heap / invalid handle state leads to arbitrary code + execution in the vmcompute.exe process. + +Attack Vector +-------------------------------------------------------------------- +An authorised guest or management client sends crafted input that +forces vmcompute to allocate and then reset a thread-pool timer or a +HANDLE wrapped by the affected wil::unique_any_t specialisations. +Because timer creation is exposed through several RPC endpoints +(StartSiloContainerWorker, WindowsContainerLaunchAndConnect, etc.) a +guest can reach the vulnerable code remotely. + +Patch Description +-------------------------------------------------------------------- +1. Corrected the deleter used in each wil::details::unique_storage + specialisation: + • HANDLE policy now calls CloseHandle. + • _TP_TIMER* policy now calls DestroyThreadPoolTimer. +2. Added exact EnumIndex validation for ProcessorFeature enums and + changed intermediate variables from signed to unsigned to prevent + negative indices. +3. Updated bounds check in InitializeProcessorFeatureSetProperty from + 0x74 to 0x7D reflecting the extended valid enum set. +4. Replaced destructors to the proper + Config::VmWorkerProcess::ProcessorFeatureSet class. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could: +• Trigger an invalid pointer dereference leading to exploitable heap + corruption, or +• Close/double-free an arbitrary kernel handle owned by the Hyper-V host + process. +Either scenario can be developed into local privilege escalation or +remote code execution inside the host OS from a guest VM context. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch aligns every resource_policy with its correct release +function, completely removing the cross-wired destructors. In addition +it introduces defensive range checks when unmarshalling enum values. +No residual paths were observed in the diff; therefore the fix is +considered effective. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-48807_vmwp.exe.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-48807_vmwp.exe.txt new file mode 100644 index 0000000..33d42f9 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-48807_vmwp.exe.txt @@ -0,0 +1,145 @@ +{'cve': 'CVE-2025-48807', 'change_count': 4, 'file': 'vmwp.exe', 'date': 1755075750.2084484, 'patch_store_uid': '8aafed3b-809e-47fe-b58d-87ad406ad8af', 'confidence': 0.27, 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-48807 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V worker process (vmwp.exe). Vulnerable code resides in +WIL templated helpers that gate feature-usage telemetry: + * wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestConfNum> + ::GetCurrentFeatureEnabledState() + * wil::details::FeatureImpl<__WilFeatureTraits_Feature_ImplVal> + ::GetCurrentFeatureEnabledState() + * wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestValidate> + ::ReportUsage() + +Vulnerability Class +-------------------------------------------------------------------- +CWE-923: Improper Restriction of Communication Channel to Intended +Endpoints (lack of parameter / state validation within an RPC-style +telemetry channel). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Feature state helpers build a 32-bit flag word (bit-field in *a2) + that is later consumed by the ReportUsage path. Two bits are + important: + 0x400 – “opt-in” + 0x800 – “device” + +2. In the pre-patch logic of + Feature_TestConfNum::GetCurrentFeatureEnabledState() a temporary + variable (v12) is set only when either + (state & 0xC00) == 0xC00 + otherwise it is zero unless bit 0x40 is also set. If v12 is true + and the secondary gate (__private_IsEnabled on + Feature_TestValidate) returns false, bit 0x400 is _cleared_. This + was intended to prevent un-validated traffic – yet the value could + be re-enabled later when execution returned to the caller because + *a2 is still writable. A race or crafted call chain therefore + allowed a forbidden 0x400 request to slip through without the + Validation feature actually being enabled. + +3. The ultimate sink is + Feature_TestValidate::ReportUsage(unsigned int *state, + unsigned __int8 kind) + implemented as: + ReportUsageToService(..., kind, 0); + The caller supplied ‘kind’ (ReportingKind) is forwarded _unchecked_ + into ReportUsageToService – an ALPC/ETW style channel handled by a + privileged Hyper-V service. Pre-patch, **any** byte value that + reached this point was accepted; the service interprets the field + as a mode/flags word. Crafted, out-of-range values steer the + service into code paths that ultimately dereference attacker- + controlled data, yielding code execution in the vmwp.exe context + (and therefore in the host partition). + +4. The second GetCurrentFeatureEnabledState variant + (Feature_ImplVal) suffers from the same missing-validation pattern + and additionally corrupts the feature gate by mixing up traits + (changes from TestGateImp to Servicing_CFONTPrintLeak in the + patch), further widening the surface. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – user-supplied 'a2' forwarded verbatim +__int64 ReportUsage(unsigned int *a1, unsigned __int8 a2) +{ + ... + v4 = a2; // no range check + ... + return ReportUsageToService(a1 + 2, 50565209i64, + (v2>>10)&1, (v2>>11)&1, + &v7, v4, 0); +} + +// before – validation bypass via v12 race +if (v12 && !v10) + *(_DWORD *)a2 &= ~0x400u; // later writable again by caller +``` +```c +// after – parameter removed, constant reporting kind (1) used +__int64 ReportUsage(unsigned int *a1) +{ + ... + v6 = 3; // fixed option flags + v5 = 0; + return ReportUsageToService(a1 + 2, 50565209i64, + (v1>>10)&1, (v1>>11)&1, + &v5, 1, 0); // hard-coded, safe +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker influences vmwp (e.g., via synthetic device or VMBus + message) so that feature state bits 0x400/0x800 are set. +2. vmwp calls Feature_*::GetCurrentFeatureEnabledState(). +3. Pre-patch gate mis-computes v12; 0x400 survives even when + validation feature is disabled. +4. Caller proceeds to Feature_TestValidate::ReportUsage() passing an + arbitrary ReportingKind byte. +5. ReportUsageToService receives the forged request and follows a + privileged code path that ends with attacker-controlled execution + inside the Hyper-V worker process (host RCE). + +Attack Vector +-------------------------------------------------------------------- +Any VM user with the ability to trigger Hyper-V management operations +(can be done from guest ring-0 via VMBus) can force vmwp.exe to emit a +feature-usage report. By manipulating feature state bits and abusing +the unchecked ReportingKind parameter the attacker opens an +unauthenticated channel to the host service and hijacks execution. +No additional privileges on the host are required. + +Patch Description +-------------------------------------------------------------------- +1. GetCurrentFeatureEnabledState(): + * Consolidates the branch condition and removes the v12 path; + therefore the 0x400 bit cannot re-appear once cleared. + * Replaces the dynamic gate (__private_IsEnabled) with a direct + ReportUsage() call, removing the timing/race window. + +2. ReportUsage(): + * Deletes the external ‘kind’ argument; the helper now takes **no + caller-controlled parameters**. + * Hard-codes ReportingKind = 1 and option flags = 3. + * Updates GetCachedFeatureEnabledState call to the correct trait + identifier. + +Security Impact +-------------------------------------------------------------------- +Before the patch, an authorised but untrusted guest could obtain code +execution in the Hyper-V worker process, breaching the isolation +boundary between guest and host (remote code execution / LPE). The +issue maps to CVE-2025-48807, rated Remote Code Execution, Hyper-V. + +Fix Effectiveness +-------------------------------------------------------------------- +Code inspection confirms that the only external input (ReportingKind) +can no longer be influenced; the call chain that allowed the 0x400 +bypass has been removed. With static values and tightened branching +logic the communication channel is now restricted to the intended +protocol, eliminating the exploit vector. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-49761_ntoskrnl.exe.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-49761_ntoskrnl.exe.txt new file mode 100644 index 0000000..be8de1a --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-49761_ntoskrnl.exe.txt @@ -0,0 +1,137 @@ +{'kb': 'KB5063878', 'cve': 'CVE-2025-49761', 'date': 1755086868.8876605, 'patch_store_uid': '203c1d43-d415-4c93-8448-a2d4f1c0abdd', 'confidence': 0.23, 'file': 'ntoskrnl.exe', 'change_count': 184} +-------------------------------------------------------------------- +CVE-2025-49761 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel (ntoskrnl.exe) – I/O manager routine +IopDequeueIrpFromFileObject(). This helper is used when an IRP is +removed from the per-FILE_OBJECT IRP queue (FsRtlCancelQueue etc.). + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free caused by incorrect manual reference +counting / premature object destruction. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. An IRP contains a Tail.Overlay.OriginalFileObject pointer at + offset +0x58 (88). The pointer is stored in the IRP with the low + three flag bits set; the real kernel object address is therefore + v8 = *(irp+88) & ~7. + +2. Prior to the patch, IopDequeueIrpFromFileObject() attempted to + dereference that FILE_OBJECT *inside the file-object spin lock* and + *inline*, bypassing the Object Manager helpers: + _InterlockedExchangeAdd64(v8-0x30, -1) + if (newcount <= 0) ObpRemoveObjectRoutine(...) + +3. The code therefore: + • Performs the final decrement while still holding the + FILE_OBJECT->SpinLock at IRQL DISPATCH_LEVEL. + • Immediately invokes the heavy object-destruction path + (ObpRemoveObjectRoutine / ObpDeregisterObject / free). + +4. If the IRP’s OriginalFileObject is identical to the file object + whose lock is held (the normal case), the routine frees the object + that owns the currently-held spin lock. The lock word becomes + stale freed memory, but the routine continues to touch it when it + later releases the lock and lowers IRQL. Concurrent threads that + attempt to acquire the same spin lock now operate on freed memory + – classic use-after-free. + +5. An attacker controlling a user thread can race two I/O paths so + that one thread dequeues the last IRP (triggering the free) while + another thread is about to acquire the same lock, leading to + arbitrary kernel memory corruption and ultimately local + privilege-escalation. + +6. The root cause is thus *premature object destruction inside a + critical section* instead of using the exported ObDereference + helpers which defer final deletion until it is safe. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch excerpt (simplified) +v8 = *(irp + 0x58) & ~7ULL; // OriginalFileObject +... +count = _InterlockedExchangeAdd64((volatile LONGLONG *)(v8-0x30), -1); +if (count <= 1) { + /* object finalisation & free while holding spin lock */ + ObpRemoveObjectRoutine(v8-0x30, 0); +} +... +_InterlockedAnd64(FileObj->SpinLock, 0); // operates on freed memory +``` + +```c +// fixed version +if (IopVelocityFlags & 1) + ObFastDereferenceObject(&KeGetCurrentThread()->... + ,v8, TAG); +else + ObfDereferenceObjectWithTag((PVOID)v8, TAG); // safe helper +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User thread + -> NtDeviceIoControlFile / ReadFile / CancelIo etc. + -> IoCancelFileOpen / IopCancelIrp / FsRtlCancelQueue IRP path + -> IopDequeueIrpFromFileObject() + (last IRP for the file) <-- vulnerability triggers + -> freed FILE_OBJECT still has active spin-lock waiters + -> subsequent kernel access -> UAF & memory corruption. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to open any file handle and issue +parallel I/O requests can cause two threads to dequeue the same FILE +OBJECT’s final IRP concurrently. A precise race frees the object +while its spin lock is still in use, allowing controlled reuse of the +freed kernel pool memory and escalation to SYSTEM. + + +Patch Description +-------------------------------------------------------------------- +1. Replaced custom spin-lock logic with + KeAcquireSpinLockRaiseToDpc / KiReleaseSpinLockInstrumented. + This guarantees balanced IRQL handling and standard lock rules. + +2. Removed the inline reference-count decrement and object-finalisation + sequence. All reference handling is now delegated to + ObfDereferenceObjectWithTag() or the fast dereference path when + IopVelocityFlags indicates it is safe. + +3. The object is therefore *never* destroyed while the file object’s + spin lock is held; final deletion is deferred until after the lock + is released, eliminating the UAF window. + +4. Miscellaneous clean-ups (return type, 64-bit lock word) are + secondary but further harden the code. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could obtain arbitrary kernel +memory write primitives via UAF, leading to elevation of privilege to +SYSTEM. The issue is reachable without special privileges beyond the +ability to issue I/O to a file handle. + + +Fix Effectiveness +-------------------------------------------------------------------- +The unsafe inline dereference path has been completely removed and +replaced with the well-tested Object Manager helper, which defers final +freeing until the object is no longer in critical sections. Locking +is now handled by standard kernel APIs. No residual code path still +performs manual object destruction, so the specific vulnerability is +considered fully remediated. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-49762_afd.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-49762_afd.sys.txt new file mode 100644 index 0000000..d645093 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-49762_afd.sys.txt @@ -0,0 +1,163 @@ +{'patch_store_uid': '230e7c66-244b-4041-b166-2157fff153a1', 'confidence': 0.25, 'cve': 'CVE-2025-49762', 'file': 'afd.sys', 'kb': 'KB5063878', 'date': 1755089264.6341386, 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-49762 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel-mode driver AFD.SYS (Ancillary Function Driver for +WinSock). + + +Vulnerability Class +-------------------------------------------------------------------- +Race condition (CWE-362) leading to use-after-free / stale pointer +access and ultimately local elevation of privilege. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AFD sockets keep per-endpoint structures that contain pointers to the +current local address, transport handles, connection objects, etc. +Several IOCTL paths copy those fields to user-supplied buffers or call +transport routines while another thread may concurrently detach (unbind) +or replace the very same fields. + +1. AfdGetAddress / AfdExtractAfdSendMsgInfo + • Pre-patch the functions read *Endpoint->LocalAddr* or the pointer + stored at Endpoint+0xA8/0xF0, then immediately memmove() that + data to the caller without holding any synchronisation primitive. + • If another thread executes AfdUnBindSocket or a TL close sequence + in parallel, the address buffer is freed and the pointer is + zeroed. The first thread continues to dereference the freed + memory, producing a use-after-free and arbitrary kernel read or + write depending on the IOCTL. + +2. AfdUnBindSocket + • The unbind path itself could run while other threads were still + using the address/transport fields because the only protection was + an interlocked state field (Endpoint+0x168). No global or per + endpoint lock was taken, so unbind could win the race, free the + storage and close handles. + +3. TL creation / completion paths + • AfdTLCreateEndpointComplete tried to cache the compartment ID only + when an optional feature switch was enabled. Missing caching + caused another window where the endpoint structure was partially + initialised but already visible to user operations. + +Patch changes +------------- +• Consistent use of KeAcquireInStackQueuedSpinLock() on the endpoint + spinlock (offset +0x38) before any dereference of the address block + and related length field, and a re-check after the lock is obtained. + +• AfdGetAddress / AfdExtractAfdSendMsgInfo now + - take the lock, + - verify that the buffer length still matches, + - copy only while still holding the lock, + - release the lock afterwards. + +• AfdUnBindSocket now + - rejects unbind for raw/connected endpoints earlier, + - denies unbind if Feature flags forbid it, + - performs TL close only when the new transport pointer is valid, + - leaves the endpoint in a consistent state before freeing memory. + +• AfdTLCreateEndpointComplete removes the feature gate – compartment + caching is now unconditional and done while holding the global + resource, guaranteeing that FsContext+0x58 is valid before the + endpoint becomes usable. + +The absence of these locks in the original code allowed two CPU cores +(or user / APC interactions) to operate on the same endpoint and reach +freed memory. Because the freed pool can be re-used by the attacker, +this results in controlled kernel pointer dereferences and privilege +escalation. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch AfdExtractAfdSendMsgInfo (excerpt) +if ((*(_DWORD *)(ep+8) & 0x100)==0 && *(_BYTE *)(ep+2)==4 && !Src) + Size = *(_DWORD *)(ep+164); +memmove(dst, *(void **)(ep+168), Size); // <-- no lock, ep may change +``` + +```c +// patched version +KeAcquireInStackQueuedSpinLock((PKSPIN_LOCK)(ep+56), &Lock); +if (Size != *(_DWORD *)(ep+164)) { ...fail... } +memmove(dst, *(void **)(ep+168), Size); +KeReleaseInStackQueuedSpinLock(&Lock); +``` + +```c +// pre-patch AfdUnBindSocket : unbind even though readers still exist +ExFreePoolWithTag(*(PVOID *)(ep+240), 'AdlF'); +*(_QWORD *)(ep+240)=0; +... +``` + +```c +// patched AfdUnBindSocket +if (FeatureX && refcount!=0) {/* deny */} +... +KeResetEvent(&Evt); +vCloseReq[0]=AfdSynchronousTlCloseRequestComplete; +if ((*OldVtab)(OldHandle,vCloseReq)==STATUS_PENDING) + KeWaitForSingleObject(...); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread A + • socket = WSASocket(); + • DeviceIoControl(afd, IOCTL_AFD_GET_ADDRESS, ...) +2. Thread B (parallel) + • DeviceIoControl(afd, IOCTL_AFD_UNBIND, ...) +3. B wins race, frees Endpoint->LocalAddr and clears pointer. +4. A continues in AfdGetAddress, dereferences freed pointer and copies + stale or attacker-controlled pool data into user buffer or into + kernel structures ⇒ memory corruption ⇒ privilege escalation. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user that can obtain a socket handle and issue +AFD IOCTLs can start two or more threads to trigger the race. No +special privileges are required beyond owning the socket. + + +Patch Description +-------------------------------------------------------------------- +1. Added spin-lock protection around every read/write of endpoint + address/transport pointers (KeAcquireInStackQueuedSpinLock / + KeRelease...). +2. Re-validated length and state after the lock (TOCTOU fix). +3. Hardened unbind: + • extra state checks, feature-flag gating, improved error paths. + • closes transport handles only after the new handle is installed. +4. Removed conditional compilation for endpoint-compartment caching – + now always initialised under the global resource. +5. Large defensive clean-up: reference counting, zeroing pointers, + resetting events, using _InterlockedAdd instead of direct decrements. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a local attacker could achieve elevation of +privilege (ring-0 code execution) by exploiting the race to perform a +use-after-free and craft fake pool objects. The bug is therefore rated +as an EoP vulnerability in the Windows kernel. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added locking removes the race window: readers cannot access the +address/transport fields while unbind is completing, and length is +re-checked before copy. Combined with stricter state validation, the +original concurrency issue is effectively mitigated. No residual race +paths were observed in the patched code. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_explorer.exe.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_explorer.exe.txt new file mode 100644 index 0000000..3a5afe8 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_explorer.exe.txt @@ -0,0 +1,117 @@ +{'file': 'explorer.exe', 'cve': 'CVE-2025-50154', 'patch_store_uid': '3c0b3495-0f83-43fc-81ff-4c4fa9ed8024', 'confidence': 0.14, 'change_count': 112, 'date': 1755086651.0510354, 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-50154 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Explorer (explorer.exe) – CTray system-tray implementation, +function CTray::UpdateTrayAndDesktopCloaking. + +Vulnerability Class +-------------------------------------------------------------------- +Logic flaw / state-management error leading to information disclosure +(CWE-200 – Exposure of Sensitive Information to an Unauthorized +Actor). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When a game enters "Gaming Full-Screen Experience" the shell is +supposed to cloak (DWMWA_CLOAK = 0x0D) every taskbar / notification +area window so they are no longer visible or capturable. The routine +responsible for this is CTray::UpdateTrayAndDesktopCloaking(). + +Prior to the patch the function only applied the cloaking attribute to +three windows: + 1. The primary taskbar window (v_hwndTray) + 2. The desktop window (v_hwndDesktop) + 3. No other taskbar host windows were considered. + +On multi-monitor systems each additional monitor owns its own taskbar +host object. Those HWNDs are held in a dynamic pointer array (HDPA) +referenced from CTray at offset *(this + 0x100) (32nd qword). Because +UpdateTrayAndDesktopCloaking() never iterated over that list, every +secondary taskbar remained uncloaked. + +If a game shared its full-screen surface (game streaming, RDP, Remote +Assistance, Miracast, etc.) the supposedly hidden secondary taskbars +were still rendered and therefore could be captured remotely. The +unintended rendering exposed live notification icons, clock, Start +button, and application titles, giving an attacker visual (and thus +spoofable) information that the user expected to be hidden. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable version +pvAttribute = v2; +if (!v_hwndTray == 0 && + CTray::IsTrayCloaked(v3) != (v2 != 0)) +{ + DwmSetWindowAttribute(v_hwndTray, 0xD, &pvAttribute, 4); + *((BYTE*)this + 928) = 1; // flag updated +} +... +if (v_hwndDesktop) + DwmSetWindowAttribute(v_hwndDesktop, 0xD, &pvAttribute, 4); +``` + +```c +// fixed version (excerpt) +v4 = (int *)*((_QWORD *)this + 32); // HDPA of host HWNDs +if (v4) +{ + v5 = *v4; + while (v5--) + { + Ptr = (HWND *)DPA_GetPtr(*((HDPA *)this + 32), v5); + if (Ptr && *((DWORD*)Ptr + 9)) // valid host HWND + DwmSetWindowAttribute(*Ptr, 0xD, &pvAttribute, 4); + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User starts or switches to a title that enables Gaming Full-Screen + Experience. +2. Explorer receives the state change and calls + CTray::UpdateTrayAndDesktopCloaking(). +3. Vulnerable build only cloaks v_hwndTray and the desktop, leaving any + secondary taskbar HWNDs visible. +4. Remote-capture, screenshot, or spoofing code obtains the pixels of + those windows, exposing UI information the user believes to be + hidden. + +Attack Vector +-------------------------------------------------------------------- +Any remote or local actor able to capture the user’s screen (RDP, +Remote Assistance, game streaming, desktop sharing, or even a local +malicious process using the Graphics Capture API) while the user is in +full-screen gaming mode on a multi-monitor setup can obtain the leaked +UI data and craft spoofed overlays. + +Patch Description +-------------------------------------------------------------------- +The patch enumerates every taskbar host HWND stored in the per-tray +HDPA and applies the DWMWA_CLOAK attribute to each valid entry. The +logic now honours Gaming Full-Screen state consistently across all +monitors, preventing residual visible taskbars. Minor refactoring also +removed a stale variable alias but the functional fix is the new +iteration over the HDPA. + +Security Impact +-------------------------------------------------------------------- +Before the fix, secondary taskbars were still rendered when the system +believed they were cloaked, leaking current time, application names, +notification icons, and allowing attackers to present spoofed UI. This +constituted an information disclosure and spoofing risk, rated by +Microsoft as CVE-2025-50154. + +Fix Effectiveness +-------------------------------------------------------------------- +The added enumeration covers every existing host HWND at the time of +the call, effectively cloaking all taskbars. Provided future taskbar +hosts call UpdateTrayAndDesktopCloaking() after creation (the current +behaviour), the issue is resolved. No residual pointers or NULL checks +appear to be missing, so the patch is considered complete. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_windows.fileexplorer.common.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_windows.fileexplorer.common.dll.txt new file mode 100644 index 0000000..b7599f2 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_windows.fileexplorer.common.dll.txt @@ -0,0 +1,118 @@ +{'date': 1755086593.4820778, 'kb': 'KB5063878', 'cve': 'CVE-2025-50154', 'patch_store_uid': '0215dbee-200b-40c9-8d33-74c3d247af0b', 'file': 'windows.fileexplorer.common.dll', 'change_count': 6, 'confidence': 0.19} +-------------------------------------------------------------------- +CVE-2025-50154 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.fileexplorer.common.dll (Windows File Explorer) +Hybrid-Compute capability detection logic – +SemanticSearchHardwareRequirementsSlim::IsHcfPresent() + +Vulnerability Class +-------------------------------------------------------------------- +Insecure configuration / insufficient input validation resulting in a +spoofing-driven information disclosure (CWE-200) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch File Explorer decided whether the system supports +Hybrid Compute Framework (HCF) exclusively by looking at an +unprotected registry location: + HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WorkloadManager\ + HCF\Status (read with KEY_QUERY_VALUE | KEY_WOW64_64KEY) + +The function SemanticSearchHardwareRequirementsSlim::IsHcfPresent() +executed the following sequence: + 1. RegOpenKeyExW() against the above path. + 2. wil::reg_view_details::try_get_value<UINT>() to fetch an unnamed + DWORD. + 3. Returned TRUE when the DWORD was non-zero; otherwise FALSE. + +No authenticity checks, ACL validation, or value sanity checks were +performed. If an attacker (local low-privilege user or a remote actor +via Remote Registry / GPO deployment) places or modifies that value, the +process will assume HCF is present although the machine may not +actually support the feature. The presence flag propagates into +several higher-level decision paths – most notably the new +Copilot/Semantic-Search experience – causing: + • Exposure of internal feature-gating state to the attacker (by + observing UI/telemetry differences). + • Ability to spoof hardware capabilities and trick the shell into + enabling code paths that were never intended to run, potentially + leading to further undefined behaviour or UI mis-representation. + +No further permission checks are executed after the registry read, so +the spoof succeeds under the security context of the File Explorer +process (typically the interactive user). Because HKLM can be +remotely writable in certain enterprise deployments, the attack can be +performed over the network, matching Microsoft’s classification as a +network-based spoofing issue. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – SemanticSearchHardwareRequirementsSlim::IsHcfPresent() +RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WorkloadManager\\HCF\\Status", + 0, 0x20019u, &hKey); +... +wil::reg_view_details::try_get_value<unsigned int>(hKey, &value, ...); +return value != 0; +``` +```c +// after patch – SemanticSearchHardwareRequirementsSlim::IsHybridComputeFrameworkPresent() +if (wil::details::FeatureImpl<...>::__private_IsEnabled(...)) + return udk::npu_detection::_anonymous_namespace_:: + IsHybridComputeFrameworkPresent(); +return 0; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker writes non-zero DWORD to the registry key stated above. +2. Victim launches File Explorer (explorer.exe). +3. explorer.exe loads windows.fileexplorer.common.dll. +4. During capability probing it calls + IsHcfPresent() -> reads forged value -> returns TRUE. +5. Explorer enables Hybrid Compute/semantic search UI paths, leaking + internal feature-state and giving the appearance of unsupported + hardware. + +Attack Vector +-------------------------------------------------------------------- +Any mechanism that allows the attacker to modify HKLM on the target +(e.g. Remote Registry, malicious MSI, mis-configured permissions, +registry reflection, or supply-chain delivered .reg payloads). No code +execution inside Explorer is required – only the ability to write the +DWORD. + +Patch Description +-------------------------------------------------------------------- +• Entire registry-based detection logic was deleted. +• New implementation relies on + udk::npu_detection::IsHybridComputeFrameworkPresent(), a trusted + system API that enumerates hardware directly rather than user + controlled configuration. +• Early exit is now executed when the related WIL feature flag is not + enabled, reducing attack surface. + +Security Impact +-------------------------------------------------------------------- +Before the fix any low-privileged or remote actor able to modify the +registry key could + 1. Force File Explorer to mis-report hardware capabilities + (spoofing). + 2. Infer internal feature flags by observing UI/telemetry changes, + leaking information that should remain internal (CWE-200). + +Although no code execution is gained, the spoofing could be chained +with other issues that assume genuine HCF support. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable code path has been entirely removed; decision making is +now delegated to a non-configurable OS routine. Unless that API is +itself vulnerable (unknown), the specific registry-spoof vector is +closed. No residual exposure via the original key remains, so the fix +is considered effective. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_windows.ui.fileexplorer.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_windows.ui.fileexplorer.dll.txt new file mode 100644 index 0000000..bcc7b36 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50154_windows.ui.fileexplorer.dll.txt @@ -0,0 +1,142 @@ +{'change_count': 231, 'confidence': 0.15, 'kb': 'KB5063878', 'patch_store_uid': 'fc627993-4587-4502-834f-627afeb669e1', 'cve': 'CVE-2025-50154', 'file': 'windows.ui.fileexplorer.dll', 'date': 1755086596.682588} +-------------------------------------------------------------------- +CVE-2025-50154 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.ui.fileexplorer.dll – specifically the File Explorer Global +Settings WinRT implementation (class +WindowsUdk.UI.Shell.implementation.FileExplorerGlobalSettings) and the +asynchronous helper GetIsAadAccountAttachedFromTokenBrokerAsync(). + + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure / Improper Access Control (CWE-200) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the DLL shipped a full WinRT producer for the public +interface IFileExplorerGlobalSettings. The automatically generated +produce()/producer() stubs were exported and their v-table contained an +entry for a method named +GetIsAadAccountAttachedFromTokenBrokerAsync(). Because the stubs were +built by the generic winrt::impl::produce/producer templates they did +not add any access-control or feature-gating logic. Any low-privileged +caller that could activate the class was therefore able to: + +1. Create a FileExplorerGlobalSettings object. +2. Call GetIsAadAccountAttachedFromTokenBrokerAsync(). +3. Inside that coroutine the code (see next section) runs + TokenBrokerInternal::FindAllAccountsAsync(), iterates through every + WebAccount, and returns true when the provider authority equals + L"organizations" (Azure AD). This directly reveals whether the + local user or device is AAD-joined. +4. The method completes without authentication, capability or feature + checks, leaking enterprise state information to any process – + including sandboxed or remote-origin code running on the machine. + +The disclosure is amplified because the information is derived from the +internal Token Broker, not from a cached registry bit, so it is always +accurate and up-to-date. + +Patch analysis shows that the producer stubs were entirely replaced by +new, **internal-only** coroutine entry points +(GetIsAadAccountAttachedFromTokenBrokerAsync__InitCoro_2 and +__ResumeCoro_1). These new helpers execute the same enumeration logic +but only run when the WIL feature flag +`Feature_FE_PI_AadAttachedUdkApi` is enabled, otherwise the coroutine is +short-circuited and no token-broker call is made. In addition, the +public WinRT v-table is no longer exported because the generic +produce()/producer() templates have disappeared. + +Finally, RefreshAllGlobalSettings() was updated to cache the result in a +private byte (LOBYTE(this[8].Ptr)) under the same feature flag. The +lock index was shifted (AcquireSRWLockExclusive( this+6 )) because the +class layout gained this new byte, confirming that the value is now an +internal implementation detail only. + +In short, the root cause was **unintended external exposure of a method +that enumerates the user’s AAD accounts without access checks.** + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old (exported) producer – nothing but a v-table exposure +auto produce(...) { + winrt::impl::produce_base(...)(a1); + *a1 = &produce<...>::`vftable'; // leaked public interface +} + +// New initialiser – note feature check and internal coroutine handle +void GetIsAadAccountAttachedFromTokenBrokerAsync__InitCoro_2(...) { + *funcPtr = _ResumeCoro_1; // coroutine entry + ... + if (wil::Feature_FE_PI_AadAttachedUdkApi::IsEnabled()) + _ResumeCoro_1(state); + else + tip2::merged_data::on_result(state+400); // early abort +} + +// Part of _ResumeCoro_1 – disclosure logic (unchanged) +accountList = TokenBrokerInternal::FindAllAccountsAsync(); +for(each account) + if (provider.Authority() == L"organizations") + return true; // machine is AAD attached +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker activates the WinRT class + WindowsUdk.UI.Shell.FileExplorerGlobalSettings. +2. Calls IFileExplorerGlobalSettings:: + GetIsAadAccountAttachedFromTokenBrokerAsync(). +3. Producer stub forwards to coroutine; TokenBrokerInternal is queried. +4. Boolean result discloses whether any AAD account exists. + + +Attack Vector +-------------------------------------------------------------------- +Local, unauthenticated code execution (any sandboxed UWP, Win32, script +or malware) could query the API and learn the AAD-join status. The +information can then be used to tailor social-engineering or network +spoofing attacks. + + +Patch Description +-------------------------------------------------------------------- +• Removed the public winrt::impl::produce / producer templates for the + IFileExplorerGlobalSettings interface. +• Added internal coroutine helpers + GetIsAadAccountAttachedFromTokenBrokerAsync__InitCoro_2 and + __ResumeCoro_1. +• Wrapped all execution paths with a WIL feature gate + Feature_FE_PI_AadAttachedUdkApi. When the feature is off the + coroutine is not executed and the result is never exposed. +• Extended the FileExplorerGlobalSettings object with a private byte + that caches the AAD-attached state; class layout and locking updated + accordingly (locks now taken at this+6 instead of this+5). + + +Security Impact +-------------------------------------------------------------------- +Before the fix any low-privileged process could discover whether the +machine/user has an organisational (Azure AD) account attached. This +violates user privacy and can aid spoofing or targeted phishing. After +the fix the information is no longer available through the public WinRT +surface unless an internal feature flag is explicitly enabled. + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable interface is gone from the exported v-table and the new +code path is subject to a feature gate that defaults to OFF. The +sensitive TokenBroker enumeration is therefore unreachable by an +unauthorised actor, fully mitigating the original disclosure. No +remaining call-sites that bypass the gate were found in the supplied +diffs. Further testing of alternate activation paths is recommended, +but the patch appears effective. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50155_wpnprv.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50155_wpnprv.dll.txt new file mode 100644 index 0000000..c596d8f --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50155_wpnprv.dll.txt @@ -0,0 +1,137 @@ +{'confidence': 0.12, 'patch_store_uid': 'bec87310-c45d-4672-9489-ba62a53bcd8d', 'file': 'wpnprv.dll', 'cve': 'CVE-2025-50155', 'date': 1755089363.619809, 'kb': 'KB5063878', 'change_count': 15} +-------------------------------------------------------------------- +CVE-2025-50155 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Push Notifications Platform (wpnprv.dll) – internal service +classes NotificationServiceImpl and CWNPTransportImpl that parse and +process incoming WNS messages and SQM/telemetry data. + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / access of a resource using an incompatible type +(CWE-843) that ultimately leads to a heap use-after-free / invalid +free (CWE-122). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Incoming WNS messages are received in + NotificationServiceImpl::OnNotification(). The message parser + returns several primitive fields (booleans, integers) plus many + heap-allocated UTF-16 strings that are stored in local pointer + variables (e.g. unsigned __int16 *v42). + +2. In the buggy build the same stack variable is re-used both as a + pointer and as an integer flag: + LODWORD(v42) = v35; // v35 == AckRequired (byte) + v42 is later assumed to be a real wchar_t* and released through a + wil::unique_storage destructor. When AckRequired is 1, v42 holds + the value 0x00000001 – an invalid heap pointer. + +3. If any subsequent operation fails (ParseNotification, logging, + COM calls, etc.) the function unwinds. wil::unique_storage’s + destructor executes and blindly calls HeapFree() on the corrupted + value stored in v42. This is a classic type confusion that + converts a small integer into a heap pointer. + +4. Exactly the same pattern existed in other paths (e.g. + CWNPTransportImpl::SQMWnpConnectAttempt and + CWNPTransportImpl::GetSqmConnectionData). Integer values were + written into pointer variables via LODWORD/LOBYTE macros and those + variables were later freed, leading to arbitrary-pointer-free. + +5. The attacker controls the execution flow that triggers the error + (for example by providing a malformed notification that forces one + of the many Throw_Hr() paths). When the integer 1 is freed the + kernel’s heap manager treats it as a pointer inside the current + process heap, resulting in memory corruption and ultimately + elevation of privilege inside the Push Notifications service. + +Affected structures / data: + • unsigned __int16 *v42 / v61 (notification channel URI) + • LPVOID lpMem / v29 / v31 in SQMWnpConnectAttempt() + • local wil::unique_storage<> and HeapFree() clean-up blocks + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v35 = *((BYTE *)v51 + 32); // boolean AckRequired +LODWORD(v42) = v35; // **pointer overwritten with int** +... +// function may throw -> clean-up: +wil::details::unique_storage<...>::~unique_storage(&v42); // HeapFree(0x1) + +// after (patch) +v36 = *((BYTE *)v51 + 32); // store flag in its own byte +// pointer variable never overwritten +``` + +```c +// CWNPTransportImpl::SQMWnpConnectAttempt – before +p_lpMem = &lpMem; +... +LODWORD(p_lpMem) = v5; // tick-count inserted into pointer +... +HeapFree(ProcessHeap, 0, lpMem); // lpMem still valid but *p_lpMem abused + +// after – dedicated integer variables added, no pointer smashing +``` +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends a specially crafted WNS payload to the target system. +2. NotificationServiceImpl::OnNotification() parses the payload; a + boolean field is extracted (AckRequired). +3. The buggy cast writes that boolean into a pointer variable. +4. Attacker influences code to hit an error path (e.g. invalid UTF-8 + string, feature gate mismatch) causing Throw_Hr() / exception. +5. Stack unwinds, wil destructors run and free the poisoned pointer. +6. Heap metadata corruption -> arbitrary memory write -> local EoP. + + +Attack Vector +-------------------------------------------------------------------- +Any local user capable of delivering a malicious push notification to +his own registered toast channel (or to another local app running +under the Push Notifications service) can trigger the bug. No admin +rights are required; the service runs with higher privileges so the +bug enables elevation. + + +Patch Description +-------------------------------------------------------------------- +• Introduced separate, correctly typed local variables for every + primitive field returned by the parser / transport layer (v36, v37, + v38, etc.). +• Removed all LODWORD/LOBYTE writes that mixed integers into pointer + variables. +• Added new scope-guard helpers (wil::ScopeExit…) to ensure correct + lifetime management without re-using variables. +• Updated WPP trace calls to use the new primitives instead of casting + pointer variables. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker-controlled boolean could be turned into +an arbitrary pointer passed to HeapFree/LocalFree. This enables heap +corruption and reliable privilege escalation from a normal user context +into SYSTEM (the Push Notifications service account). + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched build keeps all pointer variables untouched by integer +assignments and only frees memory that has been allocated. No code +path writes primitive values into pointer storage. Therefore the type +confusion and resulting invalid free are eliminated and the EoP vector +is closed. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50158_ntfs.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50158_ntfs.sys.txt new file mode 100644 index 0000000..c2e319d --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50158_ntfs.sys.txt @@ -0,0 +1,128 @@ +{'kb': 'KB5063878', 'change_count': 36, 'file': 'ntfs.sys', 'date': 1755089577.0925736, 'confidence': 0.23, 'cve': 'CVE-2025-50158', 'patch_store_uid': '4de21077-08da-4b9c-98f4-e9a52d2ce0f5'} +-------------------------------------------------------------------- +CVE-2025-50158 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows NTFS driver (ntfs.sys) – routine NtfsReadUsnJournal +handles the FSCTL_READ_USN_JOURNAL request and copies USN records to a +caller-supplied output buffer. + + +Vulnerability Class +-------------------------------------------------------------------- +Time-of-check Time-of-use (TOCTOU) race condition leading to kernel +information disclosure (CWE-367). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. User space passes an IRP for FSCTL_READ_USN_JOURNAL. The IRP + contains an output buffer pointer that resides in user memory + (offset a2+8). + +2. NtfsReadUsnJournal validates the buffer once with ProbeForRead / + ProbeForWrite but does not lock or re-validate it afterwards. The + routine then maps the pointer by calling NtfsMapUserBuffer and + stores it in v18 (pre-patch) / Address (post-patch). + +3. While processing every USN record the old code performs + memmove(v27, v35, v40); // v27 == user buffer + or, for special case records, + memmove(v27+20, (char*)Src+24, size); + The copy is executed *after* locks (VCB/ERESOURCE) have been + dropped and re-acquired multiple times (see long error-handling and + wait-for-new-length path). Between the first ProbeForWrite and the + final memmove an attacker-controlled thread can remap the user + pages or substitute the pointer through APC delivery. + +4. Because memmove operates with kernel privileges it will read from + kernel-only addresses if the pointer now references them, and will + relay that data back to the attacker once the buffer is returned to + user mode. No size checks are bypassed – the bug is purely a race + between validation and use. + +5. The disclosure is amplified for type-2 USN records where up to 0x50 + bytes are copied, including stack-resident or pool-resident padding + that may contain sensitive kernel data. + +Affected parameters / structures: + – IRP.MdlAddress / SystemBuffer (output buffer) + – local variables v18 / v27 (destination) and Src (source) + – USN_RECORD_V2 / V3 layout copy paths. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – unsafe copy once validation window has passed +if ((unsigned __int16)v17 >= 3 && v41 == 2) { + memmove(v27 + 20, (char*)Src + 24, + *(_DWORD*)Src - *((unsigned __int16*)Src + 28) - 24); +} else { + memmove(v27, v35, v40); // v27 points to user buffer +} + +// after patch – safe, atomic user access helpers +if (UserBufferIsMapped) { + RtlCopyToUser(v43, Src, copyLen); +} else { + RtlCopyVolatileMemory(v43, Src, copyLen); +} + +/* All reads of user data now use RtlReadUShortFromUser / + RtlCopyFromUser and all writes use RtlWrite*ToUser helpers. */ +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens a handle to a NTFS volume. +2. Sends FSCTL_READ_USN_JOURNAL with Method = Buffered and supplies a + user pointer as DestinationBuffer. +3. While NtfsReadUsnJournal is in the long processing loop it releases + the ERESOURCE & VCB locks. +4. Attacker uses VirtualProtect / NtMapViewOfSection to remap the same + user VA to a kernel-only physical page. +5. Kernel resumes, executes memmove, and copies arbitrary kernel memory + into the attacker’s address space. + + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged user with the ability to issue USN journal IOCTLs +against an NTFS volume. No additional privileges are required. + + +Patch Description +-------------------------------------------------------------------- +1. Replaced raw memmove/memcpy with RtlCopyToUser and + RtlCopyVolatileMemory for all writes to user buffers. +2. All reads of user-supplied structures now use RtlCopyFromUser or + RtlReadUShortFromUser ensuring the data is fetched *after* it is + committed and within user address space. +3. Added corresponding RtlWriteULong*/RtlWriteULong64ToUser helpers for + scalar stores. +4. Minor prototype change (a4 is now unsigned char) to convey boolean + semantics clearly. +5. New feature-flag path (Feature_H2E_WPA3SAE__private…) gates the safe + copy routines but defaults to enabled on supported builds. + + +Security Impact +-------------------------------------------------------------------- +The flaw allows a non-admin local attacker to read arbitrary kernel +memory fragments, possibly leaking credential material, KASLR offsets or +other sensitive data. Although no direct elevation of privilege is +performed, the disclosed information can be chained with other bugs to +achieve full kernel compromise. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the TOCTOU window by performing every user buffer +access through safe helper routines that verify the pointer belongs to +user space at the moment of the access. No remaining unprotected +memmove/memcpy calls targeting the user buffer exist in the updated +function, so the original race is effectively closed. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50159_rastls.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50159_rastls.dll.txt new file mode 100644 index 0000000..c9b073b --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50159_rastls.dll.txt @@ -0,0 +1,110 @@ +{'patch_store_uid': '6a27cc8b-3828-4b5b-abf8-9eb8f07a708f', 'file': 'rastls.dll', 'confidence': 0.27, 'change_count': 4, 'kb': 'KB5063878', 'date': 1755086332.2642686, 'cve': 'CVE-2025-50159'} +-------------------------------------------------------------------- +CVE-2025-50159 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Remote Access Service (RAS) – rastls.dll, Point-to-Point Protocol +(PPP) Extensible Authentication Protocol over TLS/PEAP certificate +handling helper GetCertContextUsingCAPI(). + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GetCertContextUsingCAPI() acquires two dependent CryptoAPI handles: + • phProv – HCRYPTPROV (CSP / KSP provider context) + • phUserKey – HCRYPTKEY obtained from the above provider + +According to CryptoAPI contract, every key handle keeps an internal +reference to its provider and therefore **the key must be destroyed +before the provider is released**. + +In the pre-patch implementation the cleanup code executes in the +wrong order: + if (phProv) CryptReleaseContext(phProv, 0); + if (phUserKey) CryptDestroyKey(phUserKey); + +Consequently CryptDestroyKey() operates on a key whose backing +provider context has already been freed. Inside the CSP this results +in a dangling pointer dereference – a classic use-after-free. Because +PPP EAP-TLS runs inside the RAS service (LocalSystem), a local attacker +who can cause RAS to load a malicious CSP/KSP or smart-card mini +driver, or merely race heap reuse, can corrupt memory in the service +process and execute arbitrary code with SYSTEM privileges. + +The bug is triggered on every error or success exit path; no other +sanity checks exist to prevent the hazardous order. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +LocalFree(v3); +LocalFree((HLOCAL)szContainer); +if (phProv) + CryptReleaseContext(phProv, 0); // provider first (wrong) +if (phUserKey) + CryptDestroyKey(phUserKey); // key after free => UAF +``` +```c +// after (feature-gated) +if (FeatureEnabled()) { + if (phUserKey) + CryptDestroyKey(phUserKey); // key first (correct) + if (phProv) + CryptReleaseContext(phProv,0); +} else { // legacy path kept for fallback + if (phProv) + CryptReleaseContext(phProv,0); + if (phUserKey) + CryptDestroyKey(phUserKey); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker causes RAS to start PEAP/EAP-TLS authentication. +2. rastls.dll -> EapPeap* -> GetCertContextUsingCAPI() to build a + CERT_CONTEXT. +3. Function allocates provider + key handles. +4. On exit, the old cleanup sequence frees provider then key. +5. Key-destruction runs on freed memory, corrupting heap/CSP data. +6. Crafted CSP can exploit the dangling pointer to execute code in the + RAS context (LocalSystem). + +Attack Vector +-------------------------------------------------------------------- +Local authorised attacker registers or supplies a malicious smart card +CSP/KSP and initiates PPP EAP-TLS authentication (e.g., with the +"rasdial" client). When GetCertContextUsingCAPI() cleans up, the CSP +receives a second call on a dangling provider pointer and gains +arbitrary memory control, leading to privilege elevation. + +Patch Description +-------------------------------------------------------------------- +The fix simply ensures correct destruction order: + • Introduces a new Wil feature flag (id 2578215227). + • When the flag is enabled, CryptDestroyKey() is called **before** + CryptReleaseContext(). + • Retains legacy order behind a feature switch for staged rollout. + • Trace-logging GUIDs were regenerated; no functional impact. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, any PPP EAP-TLS session could be used to trigger a +use-after-free inside the CryptoAPI provider. Exploitation allows +arbitrary code execution in the RAS service, yielding SYSTEM level +privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +Releasing the key before the provider removes the dangling provider +reference and eliminates the UAF condition. The mitigation is gated +by a feature flag; effectiveness therefore relies on the flag being +active on the target system. No residual paths calling provider +before key were observed in the patched diff, so the fix is considered +complete when the feature is enabled. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50161_win32k.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50161_win32k.sys.txt new file mode 100644 index 0000000..6856376 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50161_win32k.sys.txt @@ -0,0 +1,117 @@ +{'file': 'win32k.sys', 'kb': 'KB5063878', 'confidence': 0.16, 'change_count': 4, 'cve': 'CVE-2025-50161', 'date': 1755089600.5582707, 'patch_store_uid': '6bf219c7-e158-4eeb-925e-32c7e52da820'} +-------------------------------------------------------------------- +CVE-2025-50161 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys (kernel-mode graphics subsystem) +API-set redirection helpers: + • ApiSetResolveToHost + • ApiSetpResolveHost (formerly ApiSetResolveToHost_V7) + • ApiSetpGetContractKeyInfo + • ApiSetpSearchForSectionIndex_V7 + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds read-write caused by +integer-truncation and wrong structure-offset calculations +(CWE-122) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ApiSetpSearchForSectionIndex_V7 performs a binary search in a table of +section descriptors that is part of an ApiSet schema V7 block. Prior +to the patch it used the following logic: + + 1. The section count was read as a 32-bit value from (a2+4). + 2. Table address was computed as + base = *DWORD(a2) + count * entry_size – *(WORD)(a1+6) + 3. The function returned the 32-bit index of the matching section, or + 0xFFFFFFFF on failure. + +Problems: + • V7 schemas store the section count as WORD; treating it as DWORD + allows an attacker-controlled value to overflow the multiplication, + positioning base far outside the real allocation. + • The subtraction used the field at a1+6, which in V7 is *not* the + schema header size (the correct field is at offset 18). The + resulting pointer may therefore precede the allocation. + • The successful path returns the raw 32-bit index which is later + trusted by ApiSetResolveToHost_V7 to compute further pointers and + to copy host information into a caller-supplied buffer. + +A malicious process can craft an ApiSet contract key such that the +section count (WORD) wraps around, causing base to reference memory +outside the legitimate heap block. Subsequent dereferences and writes +in ApiSetResolveToHost_V7 corrupt heap memory located before or after +the schema allocation, yielding arbitrary kernel memory modification +and therefore local elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ApiSetpSearchForSectionIndex_V7 – old +v13 = *(_DWORD *)(a2 + 4); // 32-bit count (attacker controlled) +... +v17 = v15 + 8 * v16 - *(WORD*)(a1+6);// uses wrong header field +... +return *(DWORD *)(v17 + a1 + 4); // 32-bit index returned +``` +```c +// ApiSetpSearchForSectionIndex_V7 – patched +v13 = *(WORD *)(a2 + 4); // count limited to WORD +... +v17 = v15 + 8 * v16 - *(WORD*)(a1+18);// correct header offset +... +return *(WORD *)(v17 + a1 + 4); // index truncated to WORD +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode GDI call + -> CreatePerSessionWin32kCall + -> ApiSetResolveToHost + * if schema version == 7 + -> ApiSetResolveToHost_V7 (unpatched) / ApiSetpResolveHost (patched) + -> ApiSetpGetContractKeyInfo (parses attacker string) + -> ApiSetpSearchForSectionIndex_V7 (calculates index) + -> pointer arithmetic based on returned index + -> copies host info into caller buffer (heap overwrite) + +Attack Vector +-------------------------------------------------------------------- +A local, low-privilege process supplies a specially crafted ApiSet +contract key (e.g. "api-ms-win-~99999-..." containing over-sized count +fields) via a win32k system-call that ultimately invokes +ApiSetResolveToHost. No additional privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. Re-implemented ApiSetResolveToHost_V7 as ApiSetpResolveHost with a + new prototype and extensive bounds checks. +2. ApiSetpSearchForSectionIndex_V7 + • Treats section count and returned index as WORD (16-bit). + • Uses 0xFFFF as the failure sentinel instead of 0xFFFFFFFF. + • Corrects header-offset from +6 to +18. +3. ApiSetpGetContractKeyInfo adds stricter character parsing and an + early-return path when a dash ("-") precedes a numeric sequence, + preventing unintended state transitions. +4. ApiSetResolveToHost verifies header flags before delegating and + passes pre-validated parameters to the new helper. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a crafted ApiSet name could drive win32k into writing +beyond the bounds of a heap allocation, leading to memory corruption in +kernel mode. An attacker executing code in a local session could +leverage this to gain SYSTEM privileges (Elevation of Privilege). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch constrains all critical indices to 16-bit ranges, corrects +header-offset usage, installs consistent 0xFFFF sentinels, and performs +additional validation in both the contract-key parser and the top-level +resolver. These changes eliminate the arithmetic wraparound that led +to the out-of-bounds memory access; the vulnerability is considered +fully remediated. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50166_msdtcprx.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50166_msdtcprx.dll.txt new file mode 100644 index 0000000..f81f8b5 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50166_msdtcprx.dll.txt @@ -0,0 +1,123 @@ +{'kb': 'KB5063878', 'confidence': 0.19, 'patch_store_uid': '8600729a-0fa6-46e3-abfb-1d30f651940e', 'file': 'msdtcprx.dll', 'change_count': 21, 'date': 1755089313.584256, 'cve': 'CVE-2025-50166'} +-------------------------------------------------------------------- +CVE-2025-50166 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Distributed Transaction Coordinator (MSDTC) +user-mode proxy library "msdtcprx.dll" – several helper string +routines (StringCchCopy*/StringCchPrintf*/DuplicateString/…) +used by higher-level request-parsing code. + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow / wrap-around leading to out-of-bounds copy and +information disclosure. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. A family of home-grown “safe” string helpers lived in + msdtcprx.dll (e.g. StringCchCopyW, StringCchCopyA, + StringCchPrintfW, DuplicateString). They attempted to validate + the destination buffer size with 32-bit arithmetic: + (unsigned __int64)(cchDest-1) <= 0x7FFFFFFE + 2147483646 ‑ cchDest + +2. The helpers accepted the destination length as 64-bit + (size_t/ULONGLONG) but later cast it to a 32-bit signed constant + (0x7FFFFFFE). If an attacker supplied a very large length value + (>= 0x80000000) the subtraction wrapped, producing a huge + *negative* v3/v8 counter. + +3. The subsequent copy loop used that negative counter as the trip + variable, causing it to execute unbounded until the first NUL is + encountered in the source. Data beyond the end of the caller’s + buffer were therefore written/read, leaking heap contents into + protocol replies. + +4. The vulnerable helpers were broadly reused: + • AppendComsvcsFileVersion + • GetLoadedDllFileVersion + • DuplicateString + • ParseResponseFromTm / GetTmInstanceInfoFromTm … + Any network request that carried an over-sized length field + ultimately propagated into these helpers and triggered the + overflow. + +5. Because MSDTC messages are echoed back to the remote caller, the + out-of-bounds memory is disclosed over the wire – an information + disclosure flaw. + +Parameters / structures involved: + a2 / cchDest – user-controlled 64-bit length + v3/v8 counters – signed 32-bit result of (0x7FFFFFFE-a2) + destination buffer – usually 0x100-0x400 byte stack/heap array + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – StringCchCopyW +if ((unsigned __int64)(a2 - 1) > 0x7FFFFFFE) + … +else { + v3 = 2147483646 - a2; // wraps for a2 > 0x7FFFFFFE + do { + if (!(v3 + a2)) break; // v3 is negative => always true + *dest = *src++; // OOB write + } while (a2--); +} +``` +```c +// after patch – StringCchCopyW +result = StringValidateDestW(dest, cchDest, src); +if (result >= 0) + return StringCopyWorkerW_0_0(...); // size-checked helper +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client sends crafted MSDTC message with a bogus length. +2. Server-side parser copies the value into *cchDest* argument. +3. Vulnerable StringCchCopy*/Printf helper invoked. +4. 2147483646-cchDest wraps ➜ negative loop counter. +5. Copy reads/verifies beyond buffer ➜ memory disclosed in reply. + +Attack Vector +-------------------------------------------------------------------- +An authenticated network attacker (RPC/COM caller) sends a message +containing an abnormally large length (>= 0x80000000). When MSDTC +handles the request the integer wrap leads to out-of-bounds memory +being copied into the response, leaking process memory to the +attacker. + +Patch Description +-------------------------------------------------------------------- +• Entire helper family replaced with central, size-t aware + primitives StringValidateDestW/ StringCopyWorker*/ StringVPrintf… + that strictly validate (dest_len <= _countof(dest)) using + 64-bit arithmetic. +• Call-sites updated to call the new helpers and to zero-terminate + the destination on error. +• Additional range checks were added before arithmetic that builds + variable-sized buffers (e.g. ConstructMsgToTm). + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, arbitrary portions of the MSDTC process heap/stack +could be copied into protocol responses, giving an attacker insight +into memory layout and potentially sensitive data (addresses, GUIDs, +credentials). Although no code execution is achieved, the +information disclosure significantly lowers the bar for further +exploitation. + +Fix Effectiveness +-------------------------------------------------------------------- +The new helpers perform correct 64-bit size validation and bail with +an error code before any copy occurs. Destination buffers are +explicitly zeroed on failure, preventing accidental leakage. No +arithmetic uses 32-bit truncation, eliminating the wrap-around +condition. The patch therefore fully addresses the described +integer-overflow-based disclosure. + +-------------------------------------------------------------------- diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50168_win32k.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50168_win32k.sys.txt new file mode 100644 index 0000000..e7132b6 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50168_win32k.sys.txt @@ -0,0 +1,126 @@ +{'file': 'win32k.sys', 'cve': 'CVE-2025-50168', 'date': 1755089597.7626705, 'change_count': 4, 'confidence': 0.29, 'patch_store_uid': '6bf219c7-e158-4eeb-925e-32c7e52da820', 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-50168 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft win32k.sys – Api-set host-resolution helpers +(ApiSetpResolveHost, ApiSetResolveToHost, ApiSetpGetContractKeyInfo +and, most importantly, ApiSetpSearchForSectionIndex_V7). +The code is executed whenever a win32k system call needs to resolve +an Api-set DLL name (for example during client/server base‐API calls +originating from a user-mode process). + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion that results in an out-of-bounds read/heap write +(CWE-843, leads to CWE-122 heap overflow / memory corruption). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. ApiSetpSearchForSectionIndex_V7 performs a binary search over a + section table that is part of the kernel-resident Api-set schema. + +2. The table header contains a 16-bit field that stores the element + count. In the vulnerable build the function read that field as a + 32-bit DWORD : + v13 = *(DWORD *)(a2 + 4); + Consequently the high 16 bits were interpreted as valid data even + though they are uninitialised/attacker-controlled. + +3. The routine therefore believed the section contained up to 2^32 –1 + entries, while only 0…65 535 really exist. The derived index is + then multiplied with the element stride taken from *(BYTE*)(a2+8) + and added to the base pointer: + v17 = base + 8 * idx – *(WORD *)(a1 + 6); + The bogus idx causes v17 to point far beyond the legitimate table + (or even before it because the code also used a wrong base offset + of +6 instead of +18). + +4. Two DWORDs are read from the computed address and returned as the + matching section index. ApiSetpResolveHost subsequently trusts this + value and uses it to calculate further structure addresses that are + later copied back to a caller-supplied output buffer. The mix-up + between WORD and DWORD therefore constitutes a classic type + confusion that turns a logical value into a 32-bit pointer offset. + +5. Because the Api-set binary blob is mapped from user memory, a local + attacker can craft the header so that the upper half of the 32-bit + count overflows into any chosen value. This yields arbitrary read + and limited write capabilities in kernel space and allows elevation + to SYSTEM. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v13 = *(_DWORD *)(a2 + 4); // uses 32-bit count +... +return *(unsigned int *)(v17 + a1 + 4); // 32-bit index + +// after +v13 = *(unsigned __int16 *)(a2 + 4); // correct 16-bit count +... +return *(unsigned __int16 *)(v17 + a1 + 4); // 16-bit index +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process supplies a specially formatted api-set DLL name to a + win32k syscall. +2. ApiSetResolveToHost → ApiSetpResolveHost → ApiSetpSearchForSection + Index_V7. +3. Malicious header causes v13 (count) to overflow, index becomes + attacker-controlled. +4. Out-of-range v17 is dereferenced; corrupted data propagates back to + ApiSetpResolveHost. +5. Kernel copies data derived from the corrupt pointer into a user + buffer, allowing privilege escalation or information disclosure. + + +Attack Vector +-------------------------------------------------------------------- +From a low-privileged session, supply a crafted Api-set DLL string that +is at least eight characters long and embeds a forged api-set contract +key. The name is parsed by ApiSetpGetContractKeyInfo and handed to +ApiSetpSearchForSectionIndex_V7 without having validated the 16-bit vs +32-bit field width, leading to controlled memory corruption in the +kernel address space. + + +Patch Description +-------------------------------------------------------------------- +1. Correct data-type usage: + • Count field read as unsigned __int16. + • Function now returns an unsigned __int16 index; sentinel changed + from 0xFFFFFFFF to 0xFFFF. +2. Pointer arithmetic fixed: offset uses *(WORD*)(a1+18) instead of +6. +3. Additional validation in ApiSetpGetContractKeyInfo (early accept of + leading '-', explicit ban on '~', stricter digit handling). +4. Wrapper code (ApiSetpResolveHost / ApiSetResolveToHost) updated to + recognise the 0xFFFF sentinel and to perform extra bounds checks + before copying results to the caller. + + +Security Impact +-------------------------------------------------------------------- +A local attacker could craft an api-set name that forces win32k to +compute an out-of-range index, leading to arbitrary kernel memory read +and limited write. Because win32k runs in ring-0, this allows the +attacker to escalate privileges from standard user to SYSTEM and to +bypass Kernel Address Space Layout Randomisation (KASLR). + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the type confusion by aligning variable widths with +structure definitions and by introducing consistent 16-bit sentinels. +All pointer arithmetic now uses the correct header offset. Extra +validation in the helpers prevents malformed contract keys from being +processed. No remaining code path was found that still interprets the +count as 32-bit, therefore the fix appears complete and effective. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50168_win32kfull.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50168_win32kfull.sys.txt new file mode 100644 index 0000000..8b231d6 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50168_win32kfull.sys.txt @@ -0,0 +1,142 @@ +{'confidence': 0.26, 'date': 1755089661.059755, 'cve': 'CVE-2025-50168', 'kb': 'KB5063878', 'file': 'win32kfull.sys', 'change_count': 91, 'patch_store_uid': '54c644a3-337f-48f9-839a-fd90425a8708'} +-------------------------------------------------------------------- +CVE-2025-50168 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kfull.sys (kernel-mode GDI). Affected helpers are: + • PFTOBJ::HFFToPPFF + • PUBLIC_PFTOBJ::pPFFGet + • vLinkEudcPFEsWorker +All three iterate internal Persistent Font File (PFF) hash buckets +and linked lists that store font-file information. + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / use-after-free that can lead to heap out-of-bounds +access (CWE-843, CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +PFF objects are kept in per-process PFTOBJ hash buckets. Every bucket +contains a singly-linked list whose nodes have the layout + +0x00 FLINK (next PFF*) + +0x20 pwszName (const WCHAR*) + +0x28 cbName (ULONG) + +0x30 DesignVector pointer + +0x34 cbDesignVector + +0x34+ misc fields + +0x58 HFF handle copy (index 11) + +0x34 State flags (index 13) + +Before the patch the three routines walked these lists exactly as +shown below: + while (pCurrent) { + if (wanted == pCurrent->Hff) return pCurrent; + pCurrent = pCurrent->Flink; + } + +No attempt was made to verify that the pointer really points to a live +PFF. Once a user process closed the font, the object could be freed +while stale pointers remained inside the bucket or could be replaced +by attacker-controlled memory. Subsequent traversal therefore +operated on memory of an incompatible type. Dereferencing fields such +as *(DWORD*)(PFF+0x34) or memcmp(PFF->pwszName, UserBuffer, …) caused +kernel reads/writes using attacker-supplied offsets and lengths. + +The issue appears in three independent call paths: + 1. PFTOBJ::HFFToPPFF converts a font handle (HFF) back to a PFF. + 2. PUBLIC_PFTOBJ::pPFFGet performs a hash lookup by name and style. + 3. vLinkEudcPFEsWorker links EUDC (End User Defined Character) PFE + entries to base fonts. +All of them traverse the same bucket list using untrusted pointers. + +Because the objects reside in pageable pool that is both user- and +kernel-writable, an attacker can recycle the freed region with a +fake structure, control the next pointer and the embedded lengths and +thereby redirect further accesses to arbitrary kernel addresses or +create an arbitrary-length memcmp/memcpy beyond pool boundaries. This +permits elevation of privilege from user mode to SYSTEM. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// PFTOBJ::HFFToPPFF before +for (result = *(PFF **)(v2 + 8 * v3 + 40); result; + result = (PFF *)*((QWORD *)result + 1)) +{ + if (a2 == *((QWORD *)result + 11)) // uses field offset 0x58 + return result; // no validation +} +``` +```c +// PUBLIC_PFTOBJ::pPFFGet before +for (j = *v14; j; j = (PFF *)*((QWORD *)j + 1)) +{ + if (memcmp(*((void **)j + 3), a2, 2 * v8) == 0 && ...) + break; // j assumed valid +} +``` +After patch the traversal looks like: +```c +for (j = *(PFF **)(v2 + 8 * i + 40); ; j = (PFF *)*((QWORD *)res+1)) +{ + res = SkipInvalidPff(j); // new verifier + if (!res) break; + if (a2 == *((QWORD *)res + 11)) + return res; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User calls a GDI API that accepts a font handle or requests EUDC + linking (e.g., NtGdiAddFontResourceW, NtGdiRemoveFontResourceW). +2. Kernel deletes a PFF object but hash bucket still contains the + pointer, or attacker re-maps memory with controlled data. +3. Later, the same or another API calls PFTOBJ::HFFToPPFF / pPFFGet / + vLinkEudcPFEsWorker. +4. Traversal follows stale pointer, confuses attacker data for a PFF + and reads/writes through it, corrupting pool or arbitrary kernel + memory. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Requires the ability to: + • Load and unload fonts repeatedly to create freed PFFs; + • Map or allocate memory at the freed address (pool reuse); + • Invoke any GDI syscall that triggers the vulnerable look-ups. +No additional privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. Added helper SkipInvalidPff() that validates a candidate pointer + (likely checks a signature, reference count and state). +2. All three vulnerable functions now call SkipInvalidPff() before + each dereference and break out of the loop on failure. +3. Loops rewritten to use safe iterators and the current bucket base + is re-loaded after an invalid entry is skipped to avoid using stale + v2. +4. Minor clean-ups: unified hashing through iHash(), converted manual + variable juggling into for-loops. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a low-privilege process could craft or recycle pool +memory so that win32k dereferenced it as a PFF object, enabling: + • Arbitrary kernel memory read/write; + • Pool overflow from unchecked length fields; + • Elevation of privilege to SYSTEM. +Reliability of exploitation is high because the attacker controls the +structure contents and iteration count. + +Fix Effectiveness +-------------------------------------------------------------------- +The added SkipInvalidPff() gate prevents the immediate dereference of +non-PFF memory inside the three affected helpers and therefore blocks +this specific attack path. Effectiveness depends on SkipInvalidPff() +correctly authenticating the object. Other win32k code that touches +PFF lists but did not receive the change might still be vulnerable; +no such paths were visible in the supplied diff. Based solely on the +shown modifications the patch is judged adequate for these code paths. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_mrxsmb.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_mrxsmb.sys.txt new file mode 100644 index 0000000..41ff1cb --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_mrxsmb.sys.txt @@ -0,0 +1,124 @@ +{'date': 1755086356.2723403, 'file': 'mrxsmb.sys', 'patch_store_uid': 'a2af071f-5a1d-4024-b8f6-c27f25f86710', 'confidence': 0.12, 'change_count': 7, 'cve': 'CVE-2025-50169', 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-50169 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows SMB client kernel driver (mrxsmb.sys) – QUIC connect / +disconnect state-machine and redirector connection context +(RX_CONNECTION / SMB_QUIC_CONN) management code. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race condition leading to use-after-free / double-free of a +shared kernel object. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Each SMB QUIC connection context keeps an EX_RUNDOWN_REF at offset + 0xD8 (pseudonym “Conn->Rundown”). Holding rundown protection is the + only guard that prevents the structure from being freed while + asynchronous MsQuic callbacks are still operating on it. + +2. In SmbQuicInitiateAsynchronousConnect() the original implementation + unconditionally acquired two rundown counts: + ExAcquireRundownProtectionEx(&Conn->Rundown, 2); + The local variable v4 was set to the constant ‘2’ and, on every + failure path, the function executed + ExReleaseRundownProtectionEx(&Conn->Rundown, v4); + +3. Parallel code paths – most importantly + SmbQuicDisconnectEvent(), + SmbQuicConnEventPeerCertificateReceived() and + RxCeCompleteConnectRequest() – released the same rundown reference + again when they were invoked by MsQuic after a network error. The + subsequent double-release could complete rundown while other + threads still held raw pointers to the connection, generating a + use-after-free window that an attacker could exploit with network + traffic that immediately re-allocates the freed memory. + +4. Because the two releases occur on different CPUs and no additional + synchronisation is present, the window is purely time-of-check vs + time-of-use – a classic race condition (CWE-362) that leads to a + double-free (CWE-415) and ultimately to arbitrary kernel code + execution. + +5. The patch introduces a per-feature reference accounting scheme: + • The number of rundown counts acquired is now + n = 2 + Feature_IsEnabledDeviceUsage_5(); + • On failure paths the code only releases the counts when the same + feature flag is *disabled*, ensuring that every release exactly + matches a previous acquire. + • Disconnect, completion and logging routines were updated to test + the same feature flag before touching the object, eliminating the + previously uncontrolled extra release. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v4 = 2; // constant +ExAcquireRundownProtectionEx(v1+216, 2u); +... +LABEL_36: +if (v4) + ExReleaseRundownProtectionEx(v5, v4); // may run twice +``` +```c +// after +v4 = ((unsigned int)Feature_IsEnabledDeviceUsage_5() != 0) + 2; +ExAcquireRundownProtectionEx(v1+216, v4); +... +LABEL_36: +if (!Feature_IsEnabledDeviceUsage_5() && v4) + ExReleaseRundownProtectionEx(v5, v4); // balanced +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote host opens many SMB sessions to the victim. +2. Each session forces the client to enter + SmbQuicInitiateAsynchronousConnect(). +3. Crafted traffic immediately resets the QUIC connection causing + SmbQuicDisconnectEvent() to fire before the initiate path finishes. +4. Initiator and disconnect paths run concurrently: + • both execute ExReleaseRundownProtectionEx() on the same object. +5. Rundown completes, memory is freed; the still-running callback now + operates on freed memory – attacker-controlled data – leading to + kernel RCE. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated attacker sends a sequence of specially-timed SMB/QUIC +packets over the network to repeatedly start and abort connections, +probing until the double-release race is won. + +Patch Description +-------------------------------------------------------------------- +1. Reference accounting rewritten – number of rundown references taken + is now feature-dependent and decremented only by the thread that + originally incremented it. +2. All disconnect / completion paths test the same feature flag before + attempting to drop rundown references. +3. Extra bookkeeping variables (v42 etc.) added so that worker-thread + dispatch uses the correct NTSTATUS return when updating per-context + counters, preventing secondary early releases. +4. Non-functional changes (new WPP GUID tables, renamed globals) are + trace-logging only. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a remote attacker could trigger a use-after-free in +kernel mode and execute arbitrary code with SYSTEM privileges, leading +to full remote compromise (CVE-2025-50169). + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code guarantees a 1:1 relationship between +ExAcquireRundownProtectionEx() and ExReleaseRundownProtectionEx() even +under simultaneous connect/disconnect activity. Because rundown now +cannot complete prematurely, the connection object is no longer freed +while still in use, eliminating the previously exploitable race +window. No alternate path releasing the rundown without an acquire was +found in the updated code, making the fix comprehensive. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_mrxsmb20.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_mrxsmb20.sys.txt new file mode 100644 index 0000000..1794439 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_mrxsmb20.sys.txt @@ -0,0 +1,125 @@ +{'file': 'mrxsmb20.sys', 'kb': 'KB5063878', 'change_count': 3, 'confidence': 0.33, 'cve': 'CVE-2025-50169', 'date': 1755086332.7389183, 'patch_store_uid': 'ae5e0625-c9d6-4297-9b0b-81e666d6aa17'} +-------------------------------------------------------------------- +CVE-2025-50169 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows client SMBv2 driver (mrxsmb20.sys) – function +Smb2ProcessNegotiateResponse, responsible for parsing a server’s SMB2 +NEGOTIATE_RESPONSE and updating the shared MRxSmb SERVER_ENTRY +structure. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Concurrent execution using shared resource with improper +synchronisation (race condition) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Smb2ProcessNegotiateResponse must exclusively update fields inside the +shared SERVER_ENTRY (offsets 0x290 – 0x2D0: dialect, capability flags, +cryptographic material, etc.). Exclusive ownership is normally +obtained with + + SmbCeAcquireSpinLock(<serverEntry>) + +which returns at IRQL DISPATCH_LEVEL and serialises all writers. + +In the vulnerable build the call site is wrongly declared: + + SmbCeAcquireSpinLock(v81, 0x785, v22, v14); + +Four parameters – three of them originating from attacker-controlled +input – are supplied, while the routine’s prototype expects only one or +two (RCX = server, optionally RDX = tag). On x64 the surplus registers +(R8/R9) are ignored and the value 0x785 is interpreted as the tag, **but +no lock is actually taken**, leaving the shared structure unprotected. + +Because the following code immediately writes to the structure + + * WORD 0x2A0 (ServerDialect) = *(WORD *)(a5+0x44); + * DWORD0x2A4 (CapabilitiesFlags) = v34; + * WORD 0x2A2 (SecMode) = v17; + * 16-byte blob 0x290 (GUID) = *(OWORD *)(a5+0x48); + * BYTE 0x2E0 (Flags) |= 0x20 + * DWORD0x2CC (FeatureFlags) = v49; + +two or more NEGOTIATE_RESPONSE handlers that run in parallel can race +and overwrite each other’s values. While one thread later frees and +re-creates cryptographic state (hash objects, pre-auth integrity +buffers), another may still hold pointers to the previous buffers. The +result is use-after-free, double free and ultimately kernel memory +corruption that can be driven to code execution from remote. + +The absence of synchronisation applies to both authenticated and +unauthenticated sessions; a single unauthenticated attacker can exploit +this by opening multiple TCP connections and racing the NEGOTIATE +hand-shake. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – lock called with four arguments, not acquired +LOBYTE(v74) = SmbCeAcquireSpinLock(v81, 0x785, v22, v14); + +// fixed – correct prototype, lock really taken +v43 = SmbCeAcquireSpinLock(v92, 1i64); +``` + +```c +// secondary cosmetic but related clean-ups +// before: extra parameter supplied to ExAllocatePool2 +Pool2 = ExAllocatePool2(66, v20, 0x734D7253, v14); +// after: correct 3-argument form +Pool2 = ExAllocatePool2(66, v30, 0x734D7253); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker establishes several SMB sessions in parallel. +2. Each session sends a crafted SMB2 NEGOTIATE_RESPONSE with + predictable dialect/capability values. +3. All worker threads enter Smb2ProcessNegotiateResponse concurrently. +4. Because the spin lock is not acquired, threads enter the critical + section simultaneously and race while: + – modifying SERVER_ENTRY fields + – allocating and freeing hash buffers + – updating global capabilities flags. +5. One thread uses stale freed memory or corrupted flags, leading to + arbitrary kernel memory write → RCE. + +Attack Vector +-------------------------------------------------------------------- +Remote, unauthenticated network access over TCP ports 445/139. No +credentials are required – only the ability to send SMB2 NEGOTIATE and +NEGOTIATE_RESPONSE messages quickly in parallel. + +Patch Description +-------------------------------------------------------------------- +Microsoft removed the surplus arguments from synchronisation and +allocation helpers, bringing the call signatures back in line with the +authoritative prototypes: + • SmbCeAcquireSpinLock now called with only (ServerEntry,Tag), + guaranteeing exclusive ownership of the spin lock. + • ExAllocatePool2 and MRxSmbGetConfigurationBlock likewise fixed. + • Related variable re-organisation to avoid accidental re-use of + attacker-influenced values. +No other logic is changed. + +Security Impact +-------------------------------------------------------------------- +Without the fix, attackers can trigger data races that corrupt kernel +heap objects referenced by the SMB client driver, yielding elevation to +arbitrary kernel-mode code execution from the network (Remote Code +Execution). The bug affects all supported Windows versions that ship +mrxsmb20.sys. + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected call guarantees that the spin lock is truly obtained +before the shared SERVER_ENTRY is modified, removing the window for +parallel, conflicting updates. Because the only unprotected write site +was inside this function, the patch is sufficient; nevertheless, full +protection depends on all other callers using the correct prototype – +that remains unverified in this diff (state: unknown). diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_srv.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_srv.sys.txt new file mode 100644 index 0000000..9bccfd0 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50169_srv.sys.txt @@ -0,0 +1,125 @@ +{'date': 1755086338.684133, 'kb': 'KB5063878', 'file': 'srv.sys', 'change_count': 4, 'cve': 'CVE-2025-50169', 'confidence': 0.26, 'patch_store_uid': '6120763f-a8c7-49e5-a66f-f4ac932e47c8'} +-------------------------------------------------------------------- +CVE-2025-50169 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows SMB server driver (srv.sys) – routine +BlockingSessionSetupAndX (down-level Session Setup handling). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race condition (concurrent execution using shared resource +with improper synchronisation) leading to CWE-415: double free / +use-after-free. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +BlockingSessionSetupAndX is executed in the context of a work-item +for every incoming SMB Session Setup request. The routine allocates +and later releases a per-session security-context buffer that is +stored in the connection object at offset 0x3D0 ( *(_QWORD)(v4+976) +before patch ). + +Before the patch the code flow was: + +1. If no security context is present, the code allocates an opaque + buffer from non-paged pool: + NonPagedPool = SrvAllocateNonPagedPool(size,21h, dialect ) + *(_QWORD)(v4+976) = NonPagedPool; + +2. No lock is taken around the "allocate-if-null" test. Two worker + threads processing SessionSetup for the same connection at the + same time can both observe *(_QWORD)(v4+976)==NULL and will both + allocate their own pool buffer. + +3. Later, when the second thread discovers that its attempt to store + the buffer failed (because the first one already stored its own + pointer) it executes the error path that calls + SrvFreeNonPagedPool( NonPagedPool ); + while the first thread already inserted the very same pointer into + the live connection object. This produces a double free and, on a + subsequent allocation, remote-controlled memory corruption. + +4. Because the whole sequence executes while the connection resource + (at v4+544) is only held in *shared* mode, the race can be + triggered remotely by sending two Session Setup requests on two + different channels aimed at the same connection. + +Patch behaviour: + The rewritten function now enters the connection resource in + exclusive mode *before* the security-context buffer test and + allocation. It also re-checks the pointer after the lock is + obtained and only allocates when still NULL – eliminating the + race. Reference counting of the buffer is preserved and the error + paths were updated so that only the owner frees the buffer. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (simplified): +```c +if (!*(_QWORD*)(v4+976)) { // 1. unsynchronised + NonPagedPool = SrvAllocateNonPagedPool(sz,0x21,dial); + if (!NonPagedPool) goto error; + *(_QWORD*)(v4+976) = NonPagedPool; // 2. store +} +... +if (failure) { + SrvFreeNonPagedPool(NonPagedPool); // 3. double free +} +``` +After: +```c +ExAcquireResourceExclusiveLite(Resource, TRUE); // grab exclusive +if (!*(_QWORD*)(Conn+0x3D0)) { + buf = SrvAllocateNonPagedPool(sz,0x21); + if (buf) + *(_QWORD*)(Conn+0x3D0) = buf; // safe store +} +ExReleaseResourceLite(Resource); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens two SMB channels that share the same connection. +2. Sends two crafted Session Setup AndX requests in close succession. +3. Two worker threads enter BlockingSessionSetupAndX concurrently. +4. Both threads execute the unsynchronised allocate-if-null path. +5. First thread stores pointer; second thread frees its own pointer + but the first still holds that value – double free. +6. Subsequent pool allocations lead to attacker-controlled memory + reuse and remote code execution inside kernel. + +Attack Vector +-------------------------------------------------------------------- +Network. An unauthenticated attacker sends specially timed SMB +SessionSetup requests over TCP/445 to a target machine running the +vulnerable srv.sys. + +Patch Description +-------------------------------------------------------------------- +1. Re-organised the allocation logic: the connection resource is now + acquired exclusively before testing *(_QWORD)(v4+976). +2. Added second check after the lock to prevent double allocation. +3. All error paths updated so that only the thread that successfully + inserted the buffer frees it; stray allocations are freed locally. +4. Numerous defensive size/offset checks were added, but the core fix + is the introduction of correct synchronisation and bookkeeping. + +Security Impact +-------------------------------------------------------------------- +Before the fix a remote attacker could reliably induce a double free +in the non-paged pool of the kernel, leading to pool-use-after-free +and ultimately arbitrary code execution with kernel privileges. +Service crashes (BSOD) are also possible and were easy to reproduce. + +Fix Effectiveness +-------------------------------------------------------------------- +The exclusive acquisition of the connection resource serialises all +threads that reach the security-context allocation code. The re-check +under the lock ensures that at most one allocation is performed. All +follow-up free paths are symmetric and protected by reference counts. +No uncontrolled free of shared memory remains, removing the race +window. Static analysis shows no remaining code paths where +*(_QWORD)(Conn+0x3D0) can be freed twice. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50170_cldflt.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50170_cldflt.sys.txt new file mode 100644 index 0000000..353b469 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50170_cldflt.sys.txt @@ -0,0 +1,114 @@ +{'file': 'cldflt.sys', 'date': 1755089259.0807269, 'kb': 'KB5063878', 'patch_store_uid': '6bbf5beb-893e-495a-9242-532ac88ce415', 'cve': 'CVE-2025-50170', 'change_count': 3, 'confidence': 0.12} +-------------------------------------------------------------------- +CVE-2025-50170 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Cloud Files Mini-Filter Driver (cldflt.sys) – function +HsmpOpCreatePlaceholders / sub_1C005EC54. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Handling of Insufficient Permissions / Access Control +(CWE-280) leading to privileged write to user memory and local +Elevation of Privilege (EoP). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The helper that creates placeholder files receives a caller- + supplied buffer (parameter a4, Length) located in user space. +2. Before the patch the driver did: + IoAllocateMdl() + ProbeForRead( userBuf ) + MmProbeAndLockPages( mdl, KernelMode, IoReadAccess ) + The pages are therefore locked **read-only** from the point of + view of the memory manager. +3. Later in the same routine the code **writes results back into the + very same buffer**: + v52[1] = ...; + *(DWORD *)(v25+48) = ...; + *(QWORD *)(v25+72) = ...; + `v52` is an element that points directly inside the mapped + `MappedSystemVa` obtained from the MDL, i.e. the user buffer. +4. Because the pages were only probed for read access the kernel ends + up writing to user memory that may be mapped **read-only or even + executable-only** for the caller. An unprivileged process can + therefore: + • Map a read-only/executable page under its control. + • Pass its address to the vulnerable IOCTL that reaches this + routine. + • Let the kernel fill attacker–controlled data into that page + irrespective of the page protection. +5. The write is performed in kernel context, so it bypasses the user + mode page-protection mechanism, giving the attacker an + arbitrary-kernel-write-into-read-only-user-page primitive. The + attacker can place executable code in such a page and later jump + to it, effectively executing code with kernel privileges. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – pages locked read-only +MemoryDescriptorList = IoAllocateMdl(a4, Length, 0, 0, NULL); +ProbeForRead(a4, Length, 4); +MmProbeAndLockPages(MemoryDescriptorList, 1, IoReadAccess); +... +// later – kernel writes into user buffer +v52[1] = v72[1]; +*(DWORD *)(v25 + 48) = *((DWORD *)EcpContext + 10); +*(QWORD *)(v25 + 72) = *((QWORD *)EcpContext + 6); +``` +```c +// after patch – access mode decided dynamically, now writeable +v9 = IoAllocateMdl(a4, Length, 0, 0, NULL); +ProbeForRead(a4, Length, 4); +BOOL writeNeeded = sub_1C001D284() != 0; // returns TRUE when the + // routine will update the + // buffer +MmProbeAndLockPages(v9, 1, (LOCK_OPERATION)writeNeeded); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process sends FSCTL/IOCTL that ends in + HsmpOpCreatePlaceholders. +2. Function locks caller buffer with **IoReadAccess** (pre-patch). +3. Placeholder parsing succeeds and driver updates entries: + write into mapped buffer (status, timestamps, etc.). +4. If user supplied a read-only or executable page the write bypasses + intended protection, granting elevation of privilege. + +Attack Vector +-------------------------------------------------------------------- +Any local user that can open the Cloud Files device (\\.\CldFlt or +mount point) and issue the placeholder-creation request can supply a +specially protected buffer to obtain a privileged write. + +Patch Description +-------------------------------------------------------------------- +1. Replaces hard-coded IoReadAccess with a runtime decision: helper + `sub_1C001D284` returns whether the routine will modify the buffer; + the result is cast to LOCK_OPERATION, selecting IoWriteAccess when + writing is required. +2. Variable names cleaned up, but functional change is confined to the + call: + MmProbeAndLockPages( mdl, KernelMode, IoWriteAccess ) +3. No other observable logic changes are necessary for the fix. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker had a reliable arbitrary kernel write +into a user-mode page that the attacker controls but marks read-only. +By storing shellcode in an RX page and letting the driver overwrite +it, the attacker executes code in kernel mode, achieving full local +EoP. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code requests IoWriteAccess when the buffer will be modified. +With this change `MmProbeAndLockPages` raises an exception if the pages +are not writable for the caller, aborting the operation before any +kernel write occurs. This eliminates the privilege-escalation path. +No further writable operations into inadequately probed pages were +found in the patched routine. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50173_msi.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50173_msi.dll.txt new file mode 100644 index 0000000..f49f941 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50173_msi.dll.txt @@ -0,0 +1,121 @@ +{'change_count': 7, 'confidence': 0.22, 'date': 1755086690.4374554, 'cve': 'CVE-2025-50173', 'patch_store_uid': '7e78f5de-adbb-43a3-90f6-d9a32e18bcd3', 'file': 'msi.dll', 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-50173 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – msi.dll (Windows Installer) +Function: ElevatedCredentialsPromptRequired() + +Vulnerability Class +-------------------------------------------------------------------- +Logical privilege-escalation / weak authentication (CWE-1390) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ElevatedCredentialsPromptRequired() determines whether Windows +Installer should display a UAC credential prompt before continuing an +operation that ultimately executes with SYSTEM privileges. The +routine evaluates many policy and package flags and returns: + 0 – no prompt needed + 1 – credentials already supplied + 2 – prompt later (deferred) + -1 – prompt required now + +Before the patch, the decision tree contained a flaw in the branch that +handles a silent installation UI level (iuiEnum == 3, a4 == 3). When +all of the following were true: + • non-admin user (IsAdmin() == FALSE) + • installation is silent (a4 == 3) + • repair/uninstall is requested (a6 && a7) + • product is in “deployment-compliant” state 3 (a10 == 3) + • product is not group-policy managed ((a9 & 0x100000) == 0) + • no additional elevation-compat policy is enabled + +the function fell through to LABEL_54/LABEL_60 and finally returned 0 +or 1, signalling that **no credentials were necessary**. Control then +passed to msiexec.exe running as SYSTEM, giving the interactive user a +local privilege-escalation path: by triggering a silent repair of a +suitable MSI package the user executed arbitrary installer custom +actions at SYSTEM integrity without ever authenticating. + +The missing test was whether the product’s current *deployment +compliance state* actually allows “no-prompt” repair when running +silently. State 3 ("repair needed") must be blocked but was ignored. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (simplified) +if (a4 == 3) { + if (!a6 || !a7 || (a12 & 0x200) || a10 != 2 || IsEnableCompatPolicyValue()) + { + // Silent install – incorrectly concludes prompt impossible + return 0; // <== vulnerable + } + // Only deployment state 2 checked – state 3 not handled +} + +// AFTER +if (a4 == 3) { + if (Feature_2578215227_IsEnabled()) { + if (a6 && a7 && !(a12 & 0x200) && + (a10 == 2 || (a10 == 3 && !(a9 & 0x100000) && !a11)) && + !IsEnableCompatPolicyValue()) { + // Force credential prompt later; execution blocked now + v18 = 2; // prompt later + } else { + // silent install still disallowed without credentials + return 0; + } + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege user launches msiexec /quiet REPAIR=<product>. +2. Windows Installer calls ElevatedCredentialsPromptRequired(). +3. Pre-patch logic returns 0 (no prompt) for deployment state 3. +4. Service proceeds to execute repair actions as SYSTEM. +5. Custom action code supplied by attacker runs with full privilege. + +Attack Vector +-------------------------------------------------------------------- +Local – the authenticated user supplies a specially crafted or existing +MSI product in deployment-compliant state 3, invokes a silent repair or +uninstall, and gains SYSTEM privileges without UAC authentication. + +Patch Description +-------------------------------------------------------------------- +1. Added a WIL feature gate (Feature_2578215227) so the hardened path + can be enabled via servicing without functional regression. +2. Introduced extra variable (v25) and extended IsEnableCompatPolicyValue() + calls to include rcx. +3. For silent UI level (a4 == 3) the code now explicitly checks: + • a10 == 3 (deployment compliance repair/uninstall) + • group policy management flag and explicit elevation-allowed flag + • elevation compatibility policy + If conditions match, the function returns 2 (prompt later) instead of + 0, guaranteeing that a credential prompt will occur. +4. Similar tightened checks were added for the non-silent path where + a6 && a7. +5. Old debug labels re-numbered; temporary objects now v32 instead of + v31 to match new stack layout. + +Security Impact +-------------------------------------------------------------------- +Before the fix any non-admin user could obtain SYSTEM privileges by +silently repairing or uninstalling a deployment-compliant product. +After the patch, the same operation forces a credential prompt or is +blocked, eliminating the privilege-escalation path. + +Fix Effectiveness +-------------------------------------------------------------------- +The added conditional blocks ensure that all silent repair/uninstall +operations in deployment compliance state 3 (and related cases) pass +through a credential prompt path. No paths that previously returned 0 +(now labeled LABEL_54/LABEL_93) satisfy the vulnerable condition. The +feature flag allows controlled rollout but, when enabled, fully closes +the privilege gap discovered in CVE-2025-50173. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50176_dxgkrnl.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50176_dxgkrnl.sys.txt new file mode 100644 index 0000000..2dfda12 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50176_dxgkrnl.sys.txt @@ -0,0 +1,137 @@ +{'patch_store_uid': 'cee7f502-5c15-4036-bc3b-c01349c9ae48', 'confidence': 0.08, 'kb': 'KB5063878', 'file': 'dxgkrnl.sys', 'change_count': 33, 'cve': 'CVE-2025-50176', 'date': 1755089796.6052814} +-------------------------------------------------------------------- +CVE-2025-50176 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows DirectX Graphics Kernel (dxgkrnl.sys) +Functions affected: + • DxgGetHandleDataCB + • ValidateDestroyAllocation +Handle-table processing of DXGRESOURCE / DXGALLOCATION objects. + + +Vulnerability Class +-------------------------------------------------------------------- +Type-confusion / use-after-free caused by accepting a handle whose +underlying entry has an incompatible or destroyed object type. +(CWE-843, maps to the published CVE description). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Graphics objects (resources, allocations, etc.) are tracked in a per +process handle table. For each entry the low 5 bits describe the +object type, bit 0x2000 marks a "Destroyed" entry, and bit 0x4000 is a +secondary “device usage” state that indicates the entry has already +been transitioned by the driver. + +1. DxgGetHandleDataCB (code path "case 2" – resource handles) + Before the patch the code accepted a table entry when: + a) The caller-supplied handle matched the high-order security + bits 0x60, and + b) (EntryFlags & 0x2000) == 0 (not Destroyed) + and + c) (EntryFlags & 0x1F) != 0 (any non-zero type) + The pointer stored in the entry was then returned to the caller + without any further validation. + + If the driver recycled the entry by setting bit 0x4000 but left + 0x2000 cleared, the routine treated the slot as valid and returned a + pointer that no longer represented the original object. Subsequent + use of that stale pointer produced type confusion inside the kernel + and allowed controlled memory corruption. + +2. ValidateDestroyAllocation performed an almost identical test when + walking an array of allocation handles. The same missing check for + the 0x4000 transition bit let a stale or mismatched allocation + pointer pass validation, leading to the same primitive while the + function held various internal locks. + +Both paths ran while holding only a shared lock; therefore another +thread (or the GPU driver itself) could free or repurpose the backing +object between the time validation completed and the pointer was +used, turning the logical type confusion into a Use-After-Free that is +reliably exploitable for kernel RCE. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// DxgGetHandleDataCB (before) +if (((h >> 25) & 0x60) == (EntryFlags & 0x60) && + (EntryFlags & 0x2000) == 0 && // ONLY 0x2000 checked + (EntryFlags & 0x1F) != 0) // any non-zero type +{ + pRes = *(DXGRESOURCE **)(Table + Index*16); +} + +// after +if (((h >> 25) & 0x60) == (EntryFlags & 0x60) && + ((EntryFlags & 0x2000) == 0 || // accept only if not + (EntryFlags & 0x4000) != 0) && // OR specially flagged + (EntryFlags & 0x1F) == 4) // exact type check +{ + pRes = *(DXGRESOURCE **)(Table + Index*16); +} +``` +Same pattern appears in ValidateDestroyAllocation for allocation type +(0x1F == 5). + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User / driver code → D3DKMT / DXG syscall → + DxgGetHandleDataCB or ValidateDestroyAllocation → + Handle table entry validated with outdated rules → + Stale pointer for freed or wrong-type object returned → + Kernel continues operating on bogus object → memory corruption. + + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker (or a compromised user-mode graphics +component) frees a resource/ allocation, re-uses the handle after the +driver sets the 0x4000 transition bit, and calls the affected kernel +DDIs. Because the stale pointer is now interpreted as a different +kernel object type, the attacker controls subsequent kernel memory +writes and can execute arbitrary code in kernel context. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_8() gate + that switches the stricter validation on. +2. Validation now allows an entry only when + (Destroyed == 0) OR (TransitionBit 0x4000 is set) + *and* the exact expected object type matches (4 for resources, 5 + for allocations). +3. Added defensive logging (new line numbers 318, 131, etc.) and early + bail-out paths when a mismatched / transitioned entry is detected. +4. Same tightening applied throughout ValidateDestroyAllocation. +5. Additional clean-up: widened local variables, ensured all failure + paths drop acquired rundown protection and locks. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a malicious handle could bypass type checks and hand +back an arbitrary, potentially freed pointer to the graphics kernel. +The subsequent use of that pointer permits: + • Kernel pool use-after-free + • Type confusion between DXGRESOURCE / DXGALLOCATION objects + • Elevation to kernel execution (Local Privilege Escalation) + • In graphics-remoting scenarios, Remote Code Execution in the guest + VM (per MSRC classification). + + +Fix Effectiveness +-------------------------------------------------------------------- +The new logic blocks entries whose object type is wrong or whose state +has been transitioned without the additional 0x4000 flag, eliminating +the stale-pointer window that enabled the exploit. The same strict +checks are applied in every caller that walks or destroys allocation +handles, preventing alternative paths to the bug. +No bypass or obvious regression was observed in the patched code. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50176_dxgmms2.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50176_dxgmms2.sys.txt new file mode 100644 index 0000000..4615974 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50176_dxgmms2.sys.txt @@ -0,0 +1,138 @@ +{'cve': 'CVE-2025-50176', 'date': 1755089625.7686925, 'file': 'dxgmms2.sys', 'patch_store_uid': '689b7332-8ef4-4284-8807-a098fd03a7fa', 'change_count': 76, 'kb': 'KB5063878', 'confidence': 0.16} +-------------------------------------------------------------------- +CVE-2025-50176 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows ‑ dxgmms2.sys (DirectX Graphics Kernel Scheduler) +Affected helpers: + • UnwaitCpuWaitersHelper() + • HwQueueStagingList::ProcessHwQueues() + • VidSchiProcessDpcCompletedPacket() + • VidSchiCheckHeadTimeout() + • VidSchiProcessDpcPreemptedPacket() + • VidSchiExecuteMmIoFlipAtPassiveLevel() + + +Vulnerability Class +-------------------------------------------------------------------- +Type-confusion / use-after-free leading to pool corruption (heap based +buffer overflow). Root cause is the destruction of a live +HwQueueStagingList object while its embedded doubly-linked list still +contains active queue entries. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. UnwaitCpuWaitersHelper() creates a temporary HwQueueStagingList on + the stack (variable v3). After VidSchiRundownMonitoredFenceCpuWaiters() + it immediately executed: + HwQueueStagingList::~HwQueueStagingList(&v3); + +2. At this point v5==0 and v6==2 indicate that the list still holds + outstanding VIDSCH_HW_QUEUE nodes. The destructor therefore walks + those nodes and unlinks them from global scheduling lists while the + objects are *still owned* by other data structures protected by the + same spin-lock. + +3. Because the list nodes are still referenced elsewhere, the + destructor’s unlink converts them into HwQueueStagingList linkage + (BLINK/FLINK) – a classic type confusion: code later treats a + VIDSCH_HW_QUEUE object as a staging-list link. Subsequent insert or + remove operations write attacker-controlled pointer values to pool + memory (Write-What-Where), causing heap corruption and ultimately + kernel code execution. + +4. The same unsafe pattern existed in several other call-sites where a + staging list was destroyed while entries were pending (completed + packet, pre-empted packet, head-timeout path, MPU flip path). + +Parameters / structures involved + - HwQueueStagingList::Head (doubly-linked list) + - this->State (DWORD at offset +0x1C) == 2 ("Busy") + - local flags: char v5 ("empty?"), int v6 (state) used to decide + whether it is safe to tear-down. + - Spin-lock *(queueBase+1984) guarding the scheduling lists. + +Leaving the list in an inconsistent state lets an unprivileged user +build fake list entries from user-mode controlled VRAM / DMA buffers, +redirecting kernel writes. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// original UnwaitCpuWaitersHelper +VidSchiRundownMonitoredFenceCpuWaiters(&v3, a1, 0); +HwQueueStagingList::~HwQueueStagingList((HwQueueStagingList *)&v3); // <- unsafe + +// patched +VidSchiRundownMonitoredFenceCpuWaiters(&v3, a1, 0); +if (!v5 && v6) + HwQueueStagingList::ProcessHwQueues((HwQueueStagingList *)&v3, 0); +``` + +```c +// head of old ProcessHwQueues +if (*((DWORD*)this + 7) != 1) goto LABEL_2; +... +// new version +if (*((DWORD*)this + 7) == 1) { ... safer walk ... } +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode submits GPU work that blocks on a monitored fence. +2. Scheduler unwinds waiters -> UnwaitCpuWaitersHelper() executes. +3. Temporary staging list still contains HW_QUEUE records (v5==0, + v6==2). +4. Old code destructs the list, unlinking active nodes → corrupted + BLINK/FLINK in shared list. +5. Later scheduler path (e.g. VidSchiProcessDpcCompletedPacket) touches + the list, follows poisoned pointers → arbitrary kernel write / crash + → potential code execution in kernel context. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker with D3DKMT / Direct3D access programs GPU +command streams that create long-lived HW queues and monitored fences. +By forcing the race in UnwaitCpuWaitersHelper the attacker gains a +write-what-where primitive inside nt!ExRemoveHeadList style list ops, +allowing escalation to kernel-mode arbitrary code execution. + + +Patch Description +-------------------------------------------------------------------- +1. Gate the destructor call: only execute when the list is *really* + empty (condition `if(!v5 && v6) ProcessHwQueues()` instead). +2. Introduce HwQueueStagingList::ProcessHwQueues() fast-path that + properly walks each queue and clears it, then sets ListEmpty flag + before destruction. +3. Replace every unconditional `~HwQueueStagingList` in the affected + paths (completed packet, pre-empted packet, timeout, flip) with the + new safe processing routine. +4. Added additional integrity checks; if mismatch is detected code now + aborts early or fast-fails instead of corrupting memory. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a user could deliberately create a non-empty staging +list and cause its premature destruction, leading to pool memory +corruption, system crash, or arbitrary kernel code execution. Because +operations originate from GPU command submission, exploitation can be +performed from a low-privilege sandbox, turning it into a Local +Privilege Escalation or, when combined with a malicious driver, a Remote +Code Execution vector. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new guards prevent the destructor from running on non-empty lists +and ensure that all remaining HW_QUEUE nodes are safely processed first. +All previous call-sites were updated; additionally, ProcessHwQueues now +validates list integrity and uses controlled fast-fail on mismatch. +The fix fully removes the type-confusion window and no further unsafe +unlink patterns are observable in the patched binary. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50177_mqqm.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50177_mqqm.dll.txt new file mode 100644 index 0000000..1dc12d2 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-50177_mqqm.dll.txt @@ -0,0 +1,131 @@ +{'date': 1755089417.4784608, 'change_count': 11, 'patch_store_uid': '2d4fad2a-6929-40c4-8e2b-8117e27bb0cc', 'confidence': 0.07, 'file': 'mqqm.dll', 'cve': 'CVE-2025-50177', 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-50177 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Message Queuing (MSMQ) run-time, mqqm.dll – mainly the +ordering/transport layer (CMessageTransport) and the RPC / transaction +context-handle helpers (QMDo*/RPC_INT_*/PCTX_* functions). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free combined with CWE-362: Race Condition. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Outgoing packet completion is handled in + CMessageTransport::GetPacketForSendingSucceeded(). The routine + decides whether the freshly sent packet can be delivered or must be + held until the correct sequence order is met. + +2. Original logic: + if( !IsOrderNeeded(pkt) || PreSendProcess(...) == 1 ) { + …deliver immediately… + } else { + …hold… + } + + • When ordering *is* required and PreSendProcess() reports the + packet is *not* ready (return !=1), the first condition still + becomes true because of the leading logical NOT. The function + therefore delivers the packet even though the sequencing engine + has rejected it. + + • During that fast-path the code removes the packet from the hash + map, releases internal references (CReference::Release), and may + enter outgoing callbacks. Later, when the sequencing engine + resumes processing, it accesses the same transport/packet + structures that were already freed – classic use-after-free. + +3. Because MSMQ networking is asynchronous, an attacker can race the + two paths by sending specially crafted, out-of-order messages and + forcing the completion routine to trigger while the sequencing state + is inconsistent. The freed memory is still reachable from other + worker threads, enabling remote code execution within the MSMQ + service. + +4. Additional, similar lifetime issues existed in the RPC context + rundown helpers (PCTX_OPENREMOTE_HANDLE_TYPE_rundown, + RPC_INT_XACT_HANDLE_rundown) and the user-mode transaction helpers + QMDoAbortTransaction/QMDoCommitTransaction. Those functions trusted + attacker-controlled context handles and acted on possibly already + freed CTransaction objects. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old (simplified) +DeliveryPacket = CreateDeliveryPacket(this); +if (!IsOrderNeeded(DeliveryPacket) || + COutSeqHash::PreSendProcess(hash, DeliveryPacket, 0) == 1) +{ + // deliver – may free transport +} else { + SafePutPacketOnHold(this, DeliveryPacket); +} + +// New +if (IsOrderNeeded(DeliveryPacket) && + COutSeqHash::PreSendProcess(hash, DeliveryPacket, 0) != 1) +{ + SafePutPacketOnHold(this, DeliveryPacket); + GetNextEntry(this); + … + return; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote attacker sends crafted MSMQ packets with manipulated sequence + numbers. +2. Worker thread completes the send -> GetPacketForSendingSucceeded() + executes. +3. Old logic mis-classifies the packet as eligible for delivery. +4. Packet is delivered, internal refcount for CMessageTransport and + CQmPacket goes to zero, memory freed. +5. Sequencing engine or another worker thread later dereferences the + freed object -> use-after-free, leading to arbitrary code execution. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker sends a stream of MSMQ messages with +out-of-order sequence IDs to any host with the MSMQ service enabled. +No valid credentials are required; the service processes the messages +before authentication. + +Patch Description +-------------------------------------------------------------------- +1. Corrected the boolean expression: delivery is now allowed *only* when + either ordering is not needed, or PreSendProcess() approved the + packet. +2. Added early return after the SafePutPacketOnHold() path to guarantee + no further use of the packet/transport once it was queued. +3. Introduced feature-guarded helpers: + • IsDeliveryAllowed() + • PrepareAndDeliverPacket() + • PrepareDelivery() + providing additional state checks before delivery. +4. Wrapped all *rundown* and *transaction* helper functions with + CResourceManager::ValidateTransaction() to detect stale or attacker + supplied handles before dereferencing. +5. Re-instrumented WPP traces to use new GUIDs/IDs but that is cosmetic. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, remote attackers could reliably trigger a +use-after-free of CMessageTransport/CQmPacket structures, giving them the +ability to corrupt heap metadata and execute arbitrary code in the +context of the MSMQ service (runs as NETWORK SERVICE by default). The +issue is remotely exploitable and does not require authentication. + +Fix Effectiveness +-------------------------------------------------------------------- +The inverted conditional removes the logic flaw that led to premature +packet delivery. Added guard code, early returns, and transaction +validation prevent further dereference of stale objects. No remaining +code paths that deliver a packet after PreSendProcess() rejection were +observed in the patched version, suggesting the fix is effective as long +as all callers use the updated routines. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53132_win32k.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53132_win32k.sys.txt new file mode 100644 index 0000000..cdb9a21 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53132_win32k.sys.txt @@ -0,0 +1,157 @@ +{'patch_store_uid': '6bf219c7-e158-4eeb-925e-32c7e52da820', 'change_count': 4, 'kb': 'KB5063878', 'confidence': 0.18, 'cve': 'CVE-2025-53132', 'date': 1755086726.7512157, 'file': 'win32k.sys'} +-------------------------------------------------------------------- +CVE-2025-53132 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys – Api-set host-resolution logic (functions ApiSetResolve* +/ ApiSetp* dealing with V7 api-set schemas) + + +Vulnerability Class +-------------------------------------------------------------------- +Integer truncation / out-of-bounds index leading to kernel memory +corruption (CWE-190 / CWE-119) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable path starts in ApiSetResolveToHost(). When the api-set +schema version byte at *a1 equals 7 the helper +ApiSetResolveToHost_V7() is invoked. That helper in turn performs a +hash lookup through ApiSetpSearchForSectionIndex_V7() in order to find +the redirect section that matches an incoming contract name. + +Inside ApiSetpSearchForSectionIndex_V7() the number of hash entries is +obtained from *(_DWORD *)(a2+4) and stored in v13. + + v13 = *(_DWORD *)(a2 + 4); + +No further validation is done; the value is assumed to be sane. It is +used to compute a memory base and to drive a binary search which later +returns an index value: + + v15 = *(_DWORD *)a2 + v13 * *(BYTE *)(a2+8); + v17 = v15 + 8 * v16 - *(WORD *)(a1+6); + +If v13 is larger than the real table size the computed v17 pointer can +run far past the end of the section that resides inside the win32k +image. Subsequent dereferences (DWORD reads for the hash and WORD read +for the returned index) therefore cross trusted kernel memory +boundaries and corrupt the kernel address-space layout. + +Because the returned index is treated as unsigned in the caller, an +attacker can further use an oversized value to make subsequent address +calculations wrap into controlled data areas. All downstream +call-sites (v29/v25/v15 calculations in ApiSetResolveToHost_V7) rely on +the index to address secondary arrays that finally yield a UNICODE +string descriptor. Once an out-of-range index is accepted these paths +perform arbitrary kernel reads and, after length checks, potentially +kernel writes – enabling elevation of privilege. + +Key structural fields involved + * a1 – base of the loaded V7 api-set image (kernel-mode mapping) + * a2 – section descriptor whose +4 member is the 32-bit entry-count + * v13 / v14 – mutable loop bounds derived from the attacker-controlled + 32-bit entry count + * Returned WORD index used by ApiSetResolve* arithmetic + +The bug is purely arithmetic: a 32-bit entry count taken from +untrusted memory is never range-checked against the real 16-bit +allocation bounds and is later mixed with 16-bit offsets, opening the +door to pointer past-the-end dereferences. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (before) +v13 = *(_DWORD *)(a2 + 4); // untrusted 32-bit count +... +v17 = v15 + 8i64 * v16 - *(WORD *)(a1 + 6); // pointer arithmetic +... +return *(unsigned int *)(v17 + a1 + 4); // out-of-range read +``` + +```c +// fixed (after) +v13 = *(unsigned __int16 *)(a2 + 4); // count truncated to 16-bit +... +if (v13 - 1 < 0) + return 0xFFFF; // early fail +... +v17 = v15 + 8i64 * v16 - *(WORD *)(a1 + 18); // new, correct base +return *(unsigned __int16 *)(v17 + a1 + 4); // safe WORD fetch +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User mode triggers win32k path that resolves an api-set name + (e.g. CreatePerSessionWin32kCall). +2. ApiSetResolveToHost() detects schema V7 and calls + ApiSetResolveToHost_V7(). +3. ApiSetResolveToHost_V7() calls ApiSetpSearchForSectionIndex_V7() and + passes a pointer to the untrusted section header. +4. ApiSetpSearchForSectionIndex_V7() reads the 32-bit entry count, + performs unchecked pointer arithmetic, and returns an out-of-range + index. +5. Callers use that index to compute further pointers; kernel memory + past the legitimate api-set image is read/written, corrupting the + kernel. + + +Attack Vector +-------------------------------------------------------------------- +A local attacker able to feed a crafted Version-7 api-set binary into +win32k (for example by persuading the kernel to map a malicious image +from disk or memory) sets the entry-count field to a large value. When +any victim thread resolves a contract name, the unchecked arithmetic in +ApiSetpSearchForSectionIndex_V7() is hit, leading to kernel memory +corruption and privilege escalation. + +Exact delivery method (how to load an attacker-controlled V7 image) is +not visible in the diff and therefore remains unknown. + + +Patch Description +-------------------------------------------------------------------- +1. ApiSetpSearchForSectionIndex_V7() + • Treats the entry count at +4 as WORD, not DWORD. + • Consistently returns 0xFFFF on failure. + • Uses the correct structure base offset (a1+18) in pointer math. + +2. ApiSetpResolveHost() + • Re-worked version of ApiSetResolveToHost_V7() that recognises the + 0xFFFF failure code and aborts safely. + +3. ApiSetpGetContractKeyInfo() + • Additional character validation; a single leading '-' now causes + immediate success with bounded indices, and '~' is rejected unless + preceding fields are valid. This removes another path that could + leave uninitialised length values. + +4. Call-sites updated to use the new helper and sentinel (0xFFFF), + preventing accidental use of an invalid index. + + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker-supplied 32-bit entry count could drive +pointer arithmetic beyond the actual api-set image, resulting in +out-of-bounds kernel reads and writes. That enables local privilege +escalation (ring-0 arbitrary memory access) and system compromise. + +After patch the entry count is clamped to 16-bit, the sentinel 0xFFFF +propagates failure, and all dependent offsets are validated, closing +both read and write primitives. + + +Fix Effectiveness +-------------------------------------------------------------------- +The fix introduces proper width truncation, early bailout, and +consistent failure handling across the whole call chain. No remaining +code path was observed that still trusts a 32-bit entry count. The +patch therefore fully mitigates the identified overflow avenue; +residual issues are not apparent from the supplied diff. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53133_printworkflowservice.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53133_printworkflowservice.dll.txt new file mode 100644 index 0000000..7835ec2 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53133_printworkflowservice.dll.txt @@ -0,0 +1,132 @@ +{'file': 'printworkflowservice.dll', 'date': 1755086574.261769, 'cve': 'CVE-2025-53133', 'confidence': 0.19, 'patch_store_uid': 'a60bcf44-1bda-4d1c-b416-ff9675a6f222', 'change_count': 7, 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-53133 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc, implemented in printworkflowservice.dll. +Affected routines include + • PrintSupportSession::LaunchUIForJob + • several wil::details::FeatureImpl::<GetCurrentFeatureEnabledState>() + helpers that gate the LaunchUIForJob code path. + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +LaunchUIForJob is responsible for +1. switching the PrintSupportSession state to “UI-launch”, +2. creating a WorkflowJobActivationEventArgs COM object, and +3. passing that object to PrintSupportSessionCommon::LaunchAppUI, + which schedules asynchronous work that continues to reference the + same object. + +In the vulnerable build the function keeps the object in local variable +`v23` and *releases* it immediately after LaunchAppUI returns when the +normal (non-error) path is taken: + if (!error) { + if (v23) v23->Release(); //<< premature free + } +The later call to + PrintSupportSessionCommon::WaitForEventOrProcessExit(...) +still allows code running in other threads – started via LaunchAppUI – +to dereference the just-freed object. Because the object’s memory can +be re-allocated by an attacker-controlled print job, this results in a +use-after-free in the PrintWorkflowUserSvc process running with service +privileges. + +Attack pre-conditions +• Session state bit 0x20 must not be set (normal case). +• FEATURE_PrintIppPsaStabilityFixes_2505 (and, in the old build, only + that feature) must be enabled so the vulnerable branch is executed. + +Patch changes reveal two corrective measures: +1. Ownership Fix + The patched function stores the pointer in `v28` and does *not* + release it until **all** work is finished (cleanup is done via the + final lambda after WaitForEventOrProcessExit). The boolean `v32` + tracks the lifetime so the object is never released prematurely. +2. Feature Gating Tightened + The call chain is now additionally conditioned on + Feature_FileExplorer_InMarket_24A_Backport and + Feature_PrintPlatformStabilizationFixes_2508. This prevents the + vulnerable path from running unless all new feature bits are on. + +Together these changes ensure that the asynchronous code keeps a valid +reference for its whole lifetime and that the dangerous branch is no +longer reachable on systems where the fix is not present. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – object released too early +v12 = (...)->as<IInspectable>(&v23, &v24); +LaunchAppUI(...); +if (!error) { + if (v23) // still used elsewhere + v23->Release(); // UAF trigger +} +... +WaitForEventOrProcessExit(...); // other thread may deref freed obj +``` +```c +// after – pointer kept until final cleanup +v17 = (...)->as<IInspectable>(&v28, &v29); +LaunchAppUI(...); +if (!error) { + // object NOT released here any more +} +... +WaitForEventOrProcessExit(...); +... +if (v28) + v28->Release(); // done only after all work finished +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. A normal user submits / resumes a print job. +2. PrintWorkflowUserSvc calls PrintSupportSession::LaunchUIForJob. +3. WorkflowJobActivationEventArgs is created and given to + LaunchAppUI, which posts work to another thread. +4. Vulnerable code releases the object immediately. +5. The worker thread subsequently dereferences a dangling pointer – + use-after-free. + +Attack Vector +-------------------------------------------------------------------- +Local. Any authenticated user who can start a print job can trigger +LaunchUIForJob in the service context and race the premature release to +achieve controlled memory reuse. + +Patch Description +-------------------------------------------------------------------- +• Re-worked local variable layout so the activation-argument COM pointer + (`v28`) is retained until after WaitForEventOrProcessExit. +• Added lifetime flag (`v32`) and consolidated all cleanup in the final + lambda to guarantee single, late release. +• Added additional feature checks (FileExplorer_InMarket_24A_Backport + and PrintPlatformStabilizationFixes_2508) so the risky code path is + disabled unless the patched feature set is active. +• Minor refactors to wil::FeatureImpl helper functions to support the + new gating logic; they are not security-relevant themselves. + +Security Impact +-------------------------------------------------------------------- +Exploiting the UAF allows an attacker running as a normal user to gain +code execution inside PrintWorkflowUserSvc, which runs with elevated +service privileges. Successful exploitation therefore results in a +local elevation-of-privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched build keeps a valid reference to the activation event args +object for the complete duration of all asynchronous operations, +removing the dangling pointer entirely. Because the object is only +released after all work is finished, the previously exploitable race no +longer exists. Additional feature gating further reduces any residual +risk of accidentally re-enabling the vulnerable path, so the fix is +considered effective. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53133_windows.graphics.printing.workflow.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53133_windows.graphics.printing.workflow.dll.txt new file mode 100644 index 0000000..293dfd6 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53133_windows.graphics.printing.workflow.dll.txt @@ -0,0 +1,112 @@ +{'change_count': 88, 'file': 'windows.graphics.printing.workflow.dll', 'confidence': 0.26, 'date': 1755086563.190009, 'cve': 'CVE-2025-53133', 'patch_store_uid': '913e6232-d9eb-4d3e-9db1-2a47a2c1208e', 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-53133 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +PrintWorkflowUserSvc – specifically the method +winrt::Windows::Graphics::Printing::Workflow::implementation:: +PrintWorkflowJobActivatedEventArgs::EnsureSession in +windows.graphics.printing.workflow.dll. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (dangling internal pointer caused by stale +structure offsets). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +PrintWorkflowJobActivatedEventArgs is a C++/WinRT object that owns a +PrintWorkflowJobUISession pointer and an SRWLOCK used to guard it. +A refactor inserted new data members into the class, shifting every +field that followed. EnsureSession, however, was not recompiled with +the new layout and therefore used hard-coded, outdated offsets: + +• Session pointer assumed at offset 0x40 ( *((QWORD*)this + 8) ) +• SRWLOCK assumed at offset 0x68 ( this + 0x68 ) +• JobId assumed at offset 0x60 ( *((DWORD*)this + 24) ) + +At run time those offsets now reference memory that belongs to a +different object or has already been freed. When EnsureSession +believed the session pointer was null it constructed a new +PrintWorkflowJobUISession instance and wrote the IUnknown* to the +stale location. Subsequent code that dereferenced the real session +member (now at 0x48) found a dangling value, leading to a classic use +after free. Because the code runs inside PrintWorkflowUserSvc, which +is started as LocalSystem, the dangling pointer can be re-allocated by +an attacker to gain arbitrary code execution in a high-privilege +context. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch (wrong layout) +wil::RtlAcquireSRWLockShared(v8, (char *)this + 104); // +0x68 +if ( !*((_QWORD *)this + 8) ) { // session @0x40 + ... + winrt::Windows::Foundation::IUnknown::operator=((char *)this + 64, v6); +} + +// After patch (correct layout) +wil::RtlAcquireSRWLockShared(v8, (char *)this + 160); // +0xA0 +if ( !*((_QWORD *)this + 9) ) { // session @0x48 + ... + winrt::Windows::Foundation::IUnknown::operator=((char *)this + 72, v6); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User submits a print job that causes PrintWorkflowUserSvc to raise + the JobActivated event. +2. The service constructs PrintWorkflowJobActivatedEventArgs and calls + EnsureSession(). +3. EnsureSession acquires the SRW lock, mis-reads the session pointer + (dangling / previously freed). +4. Dereferencing or overwriting the stale address produces a UAF that + an attacker can shape via heap grooming. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By printing specially crafted content +and manipulating the heap (for example through repeatedly creating and +canceling print jobs) the attacker can reclaim the freed block pointed +to by the stale 0x40 pointer with controlled data, achieving arbitrary +code execution in the PrintWorkflowUserSvc process running as +LocalSystem. + + +Patch Description +-------------------------------------------------------------------- +The patch recompiles EnsureSession with the correct class layout: + +• SRWLOCK offset updated from 0x68 to 0xA0. +• Session pointer offset updated from (QWORD*)this+8 (0x40) to + (QWORD*)this+9 (0x48). +• JobId field offset updated from DWORD index 24 to 27. +• Related ETW provider IDs updated but are not security relevant. + +These changes ensure that the method accesses the intended in-object +members rather than stale memory. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could reliably turn the dangling +pointer into a write-what-where or code execution primitive inside a +SYSTEM process, resulting in elevation of privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +High. All hard-coded offsets now align with the current structure +layout, preventing the stale pointer scenario. No residual pathways +to the same bug are evident in the updated function. Full resolution +for this specific UAF is therefore achieved. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53134_afd.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53134_afd.sys.txt new file mode 100644 index 0000000..d2f180c --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53134_afd.sys.txt @@ -0,0 +1,140 @@ +{'kb': 'KB5063878', 'confidence': 0.29, 'patch_store_uid': '230e7c66-244b-4041-b166-2157fff153a1', 'change_count': 8, 'file': 'afd.sys', 'cve': 'CVE-2025-53134', 'date': 1755086279.5741298} +-------------------------------------------------------------------- +CVE-2025-53134 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-367: Time-of-check Time-of-use (TOCTOU) race condition leading to +privilege-escalation / information-disclosure from kernel memory. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Two companion routines manage the per-socket "connect data" that can be +returned to user mode through the WSASendMsg path. + +1. AfdSetConnectData() (ioctl 0x1202B, etc.) + • Allocates or reallocates an internal buffer pointed to by the + AFD_ENDPOINT field at offset 0xA8 ( *v19 ). + • Updates two fields under the endpoint spin-lock (+0x38 / +0x40): + 0xA4 -> length (DWORD) [ *(Pool2+0x8) ] + 0xA8 -> buffer pointer [ *(Pool2) ] + • Releases the lock and, if a larger buffer is needed, frees the old + pool memory after the update. + +2. AfdExtractAfdSendMsgInfo() (reached by WSASendMsg IRPs) + • If the caller supplies no user data ( Src == 0 ) the routine falls + back to the endpoint connect data: + Size = *(Endp + 0xA4); // unchecked read + Ptr = *(Endp + 0xA8); // unchecked read + No lock is held while these values are read and later used. + • The function then executes + memmove(Dst, Ptr, Size); + where Dst is inside a freshly allocated + AFDSENDMSG_TRACKER structure. + +Because there is no synchronisation between the read in +AfdExtractAfdSendMsgInfo() and a concurrent write in +AfdSetConnectData(), a window exists where: + + a. Thread-1 (SetConnectData) replaces the buffer pointer with a new + allocation and frees the old pool block after releasing the lock; + b. Thread-2 (SendMsg) reads Size/Ptr while no lock is held and later + copies from the stale/freed address. + +Results: +• Kernel out-of-bounds read → disclosure of freed or uninitialised + kernel memory to user mode. +• In certain pool reuse scenarios the copy can cross allocation + boundaries and corrupt adjacent kernel objects → potential elevation + of privilege. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// AfdExtractAfdSendMsgInfo (before) +if (*... & 0x100) == 0 && ... && !Src || (!Src && v25) +{ + Size = *(DWORD *)(Endp + 0xA4); // unchecked + ... + memmove(*(void **)(Tracker + 72), *(void **)(Endp + 0xA8), Size); +} +``` +```c +// AfdSetConnectData (before) +*Pool2 = ExAllocatePool2(..., newLen, TAG); +if (oldBuf) ExFreePoolWithTag(oldBuf, TAG); +// lock was already released -> race window open +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker thread A: + • issues IOCTL_AFD_SET_CONNECT_DATA repeatedly with alternating + sizes, forcing reallocation and free of the internal connect-data + buffer. +2. Parallel attacker thread B: + • calls WSASendMsg with no application data so that + AfdExtractAfdSendMsgInfo() copies the internal buffer. +3. With precise timing, thread B dereferences a pointer that has just + been freed or replaced by thread A, leading to an arbitrary kernel + read. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user with the ability to open an AFD socket handle +and issue the documented AFD_SET_CONNECT_DATA ioctl plus WSASendMsg. +No special privileges are required beyond owning the socket. + + +Patch Description +-------------------------------------------------------------------- +1. In AfdExtractAfdSendMsgInfo(): + • Introduces acquisition of the endpoint spin-lock + (KeAcquireInStackQueuedSpinLock(Endp+0x38)). + • Re-reads and *validates* that Size equals Endp->ConnectDataLength + while the lock is still held; returns STATUS_INVALID_PARAMETER if + the value changes or the pointer is NULL. + • Performs the memmove while still holding (or after safely releasing) + the lock, eliminating the TOCTOU window. + • Adds fallback to old behaviour behind a feature flag so the change + can be disabled at runtime. + • On error, frees the partially built tracker with tag 'AfdM'. + +2. In AfdSetConnectData(): + • Extends the lock coverage so that the old buffer is freed *before* + the pointer/length fields are updated, preventing observers from + seeing an inconsistent state. + • Adds an extra flag check (0x10000) to deny updates while a FIN is + pending. + • Harmonises small-buffer handling and tightens user-pointer probing. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an unprivileged local attacker could execute a race +leading to: +• Disclosure of freed kernel memory (information leak); +• Pool corruption with attacker-controlled data, which can be leveraged + to achieve code execution in kernel mode, resulting in a full local + privilege escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added spin-lock around both the producer (SetConnectData) and the +consumer (ExtractAfdSendMsgInfo) eliminates the inconsistent view of +pointer/length, closing the TOCTOU gap. Additional validation (non-zero +size, non-NULL pointer) blocks invalid state transitions. The fix fully +addresses the identified race condition; no bypass is evident from the +patch. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53135_dxgkrnl.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53135_dxgkrnl.sys.txt new file mode 100644 index 0000000..baac819 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53135_dxgkrnl.sys.txt @@ -0,0 +1,119 @@ +{'cve': 'CVE-2025-53135', 'change_count': 33, 'date': 1755086799.300542, 'patch_store_uid': 'cee7f502-5c15-4036-bc3b-c01349c9ae48', 'file': 'dxgkrnl.sys', 'kb': 'KB5063878', 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-53135 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows DirectX graphics kernel driver (dxgkrnl.sys) – routine +DxgkCddDisable(). All desktop GPU vendors are affected because the +routine is part of the generic kernel display infrastructure. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race condition leading to use-after-free / elevation of +privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +DxgkCddDisable() is entered from user mode (win32k) with a caller +supplied DXGDEVICE handle (parameter a1). The routine performs the +following sequence: + +1. It validates the handle under a shared push-lock stored at + DXGPROCESS+0xF8 (code: AcquireShared on Current+248). +2. After checking the handle type the function fetches the device + pointer ( DXGDEVICE* ) from the per-process handle table and saves + it to a local variable (v62 in the old build, v53 in the patched + build). +3. The push-lock is immediately released (ExReleasePushLockSharedEx), + leaving the function with only a raw pointer and **no reference** or + exclusive synchronisation. +4. A long and complex sequence of operations that follow (flushing of + presents, removal of VIDPN paths, SetDisplayModeInfo, + SetPartOfDesktop, etc.) dereference the device object and the + underlying ADAPTER_DISPLAY structure many times while other + threads are free to destroy the same object through + NtGdiDdDDIDestroyDevice / handle close. + +Because no reference is taken and only a shared lock is held during the +brief lookup window, another thread can concurrently drop the last +reference to the DXGDEVICE and free the memory. DxgkCddDisable() +continues to operate on the stale pointer, yielding a use-after-free. +An attacker running in the same session can win the race by repeatedly +opening / closing the handle while issuing the disable ioctl, leading +to controlled kernel heap corruption and ultimately SYSTEM-level +privilege escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (simplified): +```c +DXGPUSHLOCK::AcquireShared(Current + 0xF8); +ptr = HandleTable[idx].Object; // no refcount +ExReleasePushLockSharedEx(Current+0xF8); +KeLeaveCriticalRegion(); +// ptr is used for hundreds of lines without further protection +``` + +After patch: +```c +DXGPUSHLOCK::AcquireShared(Current + 0xF8); +ptr = HandleTable[idx].Object; +ExReleasePushLockSharedEx(Current+0xF8); +KeLeaveCriticalRegion(); + +// NEW: take an exclusive device lock BEFORE dereference window +DXGDEVICEACCESSLOCKEXCLUSIVECDD lock(ptr); +COREDEVICEACCESS devAcc(ptr, TRUE); // guarantees lifetime +``` +(The lock is released only after all operations are complete.) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege client issues the win32k "CDD_DISABLE" call, + eventually invoking DxgkCddDisable() in dxgkrnl. +2. Thread A (attacker) holds the ioctl and pauses after the shared lock + is released. +3. Thread B (same process) closes the same hDevice; reference count + reaches zero and DxgkDestroyDevice frees the object. +4. Thread A resumes, dereferencing freed memory and corrupting kernel + data structures under attacker control. + +Attack Vector +-------------------------------------------------------------------- +Requires local code execution with the ability to open a DXGDEVICE +handle (any desktop application). No special privileges are needed; a +sandboxed AppContainer can reach the ioctl. The race is purely +local. + +Patch Description +-------------------------------------------------------------------- +The security update introduces lifetime-safe access to the DXGDEVICE: + +• Immediately after obtaining the raw pointer the code constructs a + DXGDEVICEACCESSLOCKEXCLUSIVECDD object and a COREDEVICEACCESS guard, + both of which increment the device’s reference count and serialise + against destruction. +• The reference is held for the whole function scope and released only + after all dereferences are finished. +• Additional defensive logging and stricter handle-type checks were + added, but they are ancillary. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could obtain an arbitrary increment / +write-what-where primitive in kernel space by recycling the freed +DXGDEVICE slab with crafted data, allowing privilege escalation to +SYSTEM. The bug does not provide initial code execution but converts +any low-integrity execution context into full kernel control. + +Fix Effectiveness +-------------------------------------------------------------------- +The exclusive device lock together with the reference count eliminates +all time-of-check/time-of-use gaps between handle validation and object +use. The DXGDEVICE can no longer be freed while DxgkCddDisable() +operates on it, closing the race window and neutralising the +vulnerability. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53136_ntoskrnl.exe.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53136_ntoskrnl.exe.txt new file mode 100644 index 0000000..2f166f1 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53136_ntoskrnl.exe.txt @@ -0,0 +1,135 @@ +{'cve': 'CVE-2025-53136', 'date': 1755089707.9361336, 'patch_store_uid': '203c1d43-d415-4c93-8448-a2d4f1c0abdd', 'kb': 'KB5063878', 'file': 'ntoskrnl.exe', 'confidence': 0.33, 'change_count': 184} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel memory-manager routine MiGetPerfectColorHeadPage. +The routine is used during page allocation to pull a page with the +"perfect colour" from a per-colour free/zero queue. + + +Vulnerability Class +-------------------------------------------------------------------- +Logical error / faulty validation that can lead to information +disclosure (CWE-200). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +MiGetPerfectColorHeadPage walks a set of per-colour PFN queues and +returns a page frame whose colour matches the caller-supplied value +(a1+0x20, named Colour). For correctness, the routine must also +ensure that the PFN belongs to the same memory-partition / NUMA +node that the caller is currently operating on ( *a1 points to the +MI_PARTITION structure, field 0x3A98). In the original code the +check that enforces this partition match is expressed as + + if ( MmInitSectionResAvailLeakFixEnabled && + PartitionArray[index] != CurrentPartitionPointer || + (PFN->Color & 7) != Colour ) + { reject page } + +The leading boolean operator chain is wrong: whenever the global +flag MmInitSectionResAvailLeakFixEnabled is *clear* (the normal +runtime case) the whole left sub-expression collapses to FALSE and +only the colour comparison remains significant. A page that has +the right colour but belongs to another partition therefore passes +validation and is returned to the caller while still containing the +previous partition’s contents. Because pages on the Free list are +not guaranteed to be zeroed, user mode code that later maps the page +receives stale kernel data. + +Patch reverses the logic and removes the dependency on the global +flag. The new code accepts a page only when *both* conditions are +true: + + if ( (PFN->Color & 7) == Colour ) + if ( PartitionArray[index] == CurrentPartitionPointer ) + { accept page } + +If either the colour or the partition pointer does not match the +request, the PFN lock is released and the search continues. + +Additional hardening changes introduced together with the fix: +1. Function signature now takes the MI_PARTITION pointer by value to + make aliasing explicit. +2. Validation and spin-lock operations are re-structured and + instrumented, but these are secondary to the logical correction + above. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if ( MmInitSectionResAvailLeakFixEnabled && + *((_QWORD *)qword_140E2FCC8 + + ((*(_QWORD *)(v10 + 40) >> 43) & 0x3FF)) != + *(_QWORD *)(v19 + 14984) + || (*(_BYTE *)(v10 + 34) & 7) != (DWORD)v4 ) +{ + /* reject */ +} + +// after +if ( (*(_BYTE *)(v11 + 34) & 7) == v25 ) +{ + if ( *((_QWORD *)qword_140E2FD88 + + ((*(_QWORD *)(v11 + 40) >> 43) & 0x3FF)) == + *(_QWORD *)(v26 + 14984) ) + { + /* accept */ + } +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process requests memory (e.g. VirtualAlloc / page-fault). +2. Kernel ultimately calls MiGetPerfectColorHeadPage to pull a page + from the per-colour free queue. +3. On an affected build the colour matches but the partition pointer + does not; the faulty OR logic still deems the PFN acceptable. +4. PFN is unlinked and mapped into the caller’s address space without + being zeroed, exposing residual data. + + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged attacker repeatedly allocates and reads large +anonymous memory regions. Some returned pages will belong to a +foreign partition and contain leftover kernel information which can +be harvested from user mode. + + +Patch Description +-------------------------------------------------------------------- +• Re-wrote validation as a strict two-step AND comparison that + requires both page colour and partition match. +• Removed dependency on the runtime flag + MmInitSectionResAvailLeakFixEnabled. +• Added helper MiGetColorHeadBase and updated signature to pass the + partition pointer by value. +• Replaced raw spin-lock sequences with instrumented variants for + additional robustness; unrelated to the leak but hardens the code. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, memory pages from another memory partition could +be handed to an untrusted user process still containing sensitive +kernel data, allowing disclosure of kernel pointers, object content +or other privileged information. The flaw does not grant direct +code execution but undermines KASLR and can aid further attacks. + + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected Boolean logic ensures a page is returned only when +both colour and partition match, eliminating the possibility of +returning a foreign PFN. No alternative path in the function can +bypass the new check, therefore the patch fully addresses the +information disclosure issue. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53137_afd.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53137_afd.sys.txt new file mode 100644 index 0000000..b9564c8 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53137_afd.sys.txt @@ -0,0 +1,137 @@ +{'date': 1755089240.4609733, 'file': 'afd.sys', 'kb': 'KB5063878', 'patch_store_uid': '230e7c66-244b-4041-b166-2157fff153a1', 'cve': 'CVE-2025-53137', 'confidence': 0.14, 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-53137 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel-mode driver +File: afd.sys (Ancillary Function Driver for WinSock) +Functions affected: AfdUnBindSocket, AfdGetAddress, AfdQueryHandles, +AfdExtractAfdSendMsgInfo, AfdBind, AfdTLCreateEndpointComplete, +DriverEntry and helpers. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (UaF) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The TL (Transport-Layer) endpoint held inside the AFD endpoint + structure is changed during socket un-bind. The original routine + (AfdUnBindSocket, before patch) performs the following sequence on + the success path of the TL_CREATE request when the endpoint owns a + dual-mode transport (EAFL_TL_BASE): + + a. Builds a synchronous TL_CREATE IRP and waits for completion. + b. Stores returned pointers ( v38 & v39 – TL FILE_OBJECT and + function table ) in local variables only. + c. **Replaces** E+0x10 / E+0x18 (SavedFileObject / FunctionTbl) + with the newly returned pointers **before** the old pair is + closed. + d. Immediately closes the old TL endpoint through an indirect + call (*v15)(oldFileObject, CloseReq) without keeping any + reference on that object. + +2. Because no extra reference is taken, the close routine may free the + old TL endpoint and the function-pointer table while the caller is + still inside AfdUnBindSocket. Subsequent indirect calls through the + same table (or access to other fields inside the freed object) are + therefore made on freed memory – a classic UaF. + +3. An unprivileged local client can reliably reach this code path by + issuing IOCTL_AFD_UNBIND and crafting the address data so that the + driver enters the (v9 & 0x100) branch (IPv6 dual stack / base + endpoint case). In parallel the attacker reclaims the freed pool in + user-controlled data, converting the UaF into an arbitrary kernel + code execution primitive and elevating privileges. + +4. Additional UaF windows existed in the IPv4-mapped branch + ( (v9 & 0x200) != 0 ) where (*v19)(oldFileObject, CloseReq) is + invoked unconditionally, even when the returned v19 pointer is + NULL. + +5. No privilege checks are performed; the bug is reachable from any + user process that holds a Winsock handle. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// afd!AfdUnBindSocket – before patch +v14 = *(_QWORD *)(ep + 0x10); // old FO +v15 = *(FN **)(ep + 0x18); // old function table +*(_QWORD *)(ep + 0x10) = v38; // new FO +*(_QWORD *)(ep + 0x18) = v39; // new table +... +// close old TL endpoint – UaF window +KeResetEvent(&Evt); +v31[0] = AfdSynchronousTlCloseRequestComplete; +if ( (*v15)(v14, v31) == STATUS_PENDING ) // v15 already freed + KeWaitForSingleObject(&Evt,...); +``` + +```c +// after patch +v21 = *(_QWORD *)(ep + 0x10); +v22 = *(FN **)(ep + 0x18); +... +if (FeatureFlag && v22) { // new NULL / feature gate check + KeResetEvent(&Evt); + Req[0] = AfdSynchronousTlCloseRequestComplete; + if ( (*v22)(v21, Req) == STATUS_PENDING ) + KeWaitForSingleObject(&Evt,...); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User calls bind() then WSAIoctl( SIO_AF_UNBIND ) on the same + socket. +2. afd.sys -> AfdDispatchDeviceControl -> AfdUnBindSocket. +3. Driver allocates/initialises synchronous TL_CREATE request. +4. On success, driver swaps the TL endpoint pointers and closes the old + endpoint via function table obtained from the **freed object**. +5. Freed object memory is re-used – UaF leads to code execution in + kernel context. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. No special privileges are needed beyond the +ability to open a Winsock socket and issue IOCTL_AFD_UNBIND. + + +Patch Description +-------------------------------------------------------------------- +• Added feature-flag gates and NULL checks before indirect calls to + TL-provider function tables (v22/v15). +• Deferred pointer replacement until **after** successful close, or + closes only when a valid function table is present. +• Introduced additional reference counting via AfdRefTLBaseEndpoint / + AfdDerefTLBaseEndpoint so the old TL FILE_OBJECT cannot be freed + prematurely. +• Hardened many ancillary paths (Bind, GetAddress, QueryHandles, Send, + DriverEntry) to block unbind in unsafe states and to re-allow it only + under guarded conditions (AfdPreventUnbind / AfdReallowUnbind). + + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could trigger a kernel use-after-free +and achieve arbitrary code execution in kernel-mode, yielding SYSTEM +privileges (Elevation of Privilege). + + +Fix Effectiveness +-------------------------------------------------------------------- +The new code ensures that: +• The old TL endpoint is closed only when its function table pointer is + still valid (non-NULL) and a reference is held. +• Additional feature gating rejects dangerous unbind requests in mixed + socket states. +• Pointer swaps are now atomic with respect to lifetime management. +No further UaF condition is reachable via IOCTL_AFD_UNBIND under the +same scenario, indicating the patch fully mitigates CVE-2025-53137. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53140_fstx.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53140_fstx.dll.txt new file mode 100644 index 0000000..630e401 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53140_fstx.dll.txt @@ -0,0 +1,138 @@ +{'patch_store_uid': 'a20777f6-3ab9-42da-8608-fab6dec7fed4', 'confidence': 0.34, 'date': 1755086305.05265, 'cve': 'CVE-2025-53140', 'kb': 'KB5063878', 'file': 'fstx.dll', 'change_count': 3} +-------------------------------------------------------------------- +CVE-2025-53140 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kernel – Common Log-File System (CLFS) / Kernel Transaction +Manager (tm.sys ⇢ fstx.dll). Affected helpers are the marshaling +routines that fetch log records and restart areas: + • CClfsMarshallingContext::ReadNextLogRecord() + • CClfsMarshallingContext::ReadLogRecord() + • CClfsMarshallingContext::ReadRestartArea() + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free caused by a racy, home-grown lock bit that can +be cleared while other threads still hold pointers into the released +log-buffer. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. CLFS keeps log blocks in user-mode buffers owned by CLogIocb. A + caller that wants to parse a block must set a “busy” flag (bit 0x80) + in the block header word that lives 16 bytes in front of the buffer. + The flag is set/cleared with an _InterlockedCompareExchange on + (blockBase-16). + +2. Pre-patch Read* helpers compute the address of that header as + header = *(ctx->pTable128) + lsn.offset - 16 + where pTable128 is taken from ctx+0x80. When the compare/exchange + succeeds the routine assumes exclusive ownership, parses the block + and finally clears the bit. + +3. Two flaws existed: + a) The address used for the CAS is wrong when the log wraps – + v45+v43 (in ReadNextLogRecord) or v11-16 (in + ReadRestartArea) can point to a buffer that already belongs to a + different CLogIocb that has been freed and possibly re-allocated + for attacker-controlled data. + b) The code never re-validated the pointer after ReadMarshalled­ + LogRecord() or NtRead*Internal() may have detached and freed the + IOCB on error paths. Subsequent dereferences of + a2[13] / v19 / v73 therefore operate on a freed object. + +4. An attacker running in a low-privilege container can create + specially-shaped CLS_LSN values that cause the wrap-around and make + the CAS operate on memory that is later freed from another thread. + Shortly afterwards ReadNextLogRecord()/ReadRestartArea() continues + to use the stale pointer and corrupts pool memory, enabling + elevation of privilege. + +5. The UAF is reachable from transactional file-system APIs such as + Create­Transaction + WriteFileTransacted, hence is locally + triggerable without special rights. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – ReadNextLogRecord excerpt +v45 = *(unsigned int *)(a2[24].ullOffset + 128); +while (1) { + v46 = *(_DWORD *)(v45 + v43.ullOffset - 16); // wrong base + v47 = _InterlockedCompareExchange( + (volatile signed __int32 *)(v45+v43.ullOffset-16), + v46 | 0x80, v46); + if ((v47 & 0x80) != 0) return 170; // already free + if (v47 == v46) break; // we "own" it +} +... +// later, after possible free of IOCB, stale a2[13] is deref’d +*(_BYTE *)(a2[13].ullOffset + 36); +``` +```c +// after – corrected logic +IsEnabledDeviceUsageNoInline(); // new kernel gate +v46 = (volatile signed __int32 *)(v42 + v40.ullOffset - 16); +v47 = _InterlockedCompareExchange(v46, + *(_DWORD *)(v43-16)|0x80, + *(_DWORD *)(v43-16)); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User calls NtCreateTransaction + transactional FS APIs generating + many small log records → CLFS produces wrap-around LSNs. +2. Kernel recovery thread (or another user caller of ClfsReadRecord) + invokes CClfsMarshallingContext::ReadNextLogRecord. +3. Function acquires bogus 0x80 lock on old buffer, later frees buffer + on error path but still dereferences the object → pool corruption → + arbitrary code execution in kernel. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By crafting a sequence of CLS_LSNs that +force log-wrap and concurrent reads, the attacker races the recovery +thread, hitting the stale pointer dereference. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced helper Feature_Atomic_CLFS_Read_Fix__private_* + to decide the correct base address for the atomic operation. +2. Re-computed header pointer with the value returned from the helper + (v42 + v40.ullOffset – 16) instead of the stale table address. +3. Converted counted for-loops to while-loops that increment an + explicit retry counter variable (v41/v9) – no accidental reuse of + loop counter after free. +4. Re-structured variable usage so that record-header pointers are + stored only after the CAS succeeds and remain valid for the lifetime + of the IOCB. +5. All three exposed read helpers (NextLogRecord, ReadLogRecord, + ReadRestartArea) received identical fixes. + + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could cause a Use-After-Free in kernel +pool memory from an unprivileged context, leading to arbitrary kernel +code execution and therefore a full local elevation of privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the stale pointer window by: + • Using the correct buffer address for the atomic lock. + • Retrying the CAS with bounded attempts and gating through the new + helper. + • Ensuring IOCB pointers are not dereferenced after they might have + been freed. +No residual path that dereferences freed IOCBs remains reachable, so +the UAF is effectively eliminated. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53140_tm.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53140_tm.sys.txt new file mode 100644 index 0000000..df6e610 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53140_tm.sys.txt @@ -0,0 +1,138 @@ +{'change_count': 2, 'confidence': 0.27, 'file': 'tm.sys', 'patch_store_uid': '2e05e9ef-cf22-489d-8416-a4d4c90d9a80', 'kb': 'KB5063878', 'cve': 'CVE-2025-53140', 'date': 1755086330.0185392} +-------------------------------------------------------------------- +CVE-2025-53140 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kernel – Transaction Manager driver (tm.sys) +Function involved: TmpInitializeEnlistment / +TmpInitializeEnlistment$fin$0 + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (dangling pointer on Transaction Manager +object) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +TmpInitializeEnlistment is reached from the NtCreateEnlistment system +call when a new enlistment is created for a Resource Manager (RM) +inside a Transaction (KTRANSACTION). + +1. The code copies the RM’s Transaction-Manager pointer found at + *(RM + 0x168) into the local variable v11 ( type _KTM* ). + It correctly bumps the reference count with + ObfReferenceObject(v11). + +2. If the current Transaction already belongs to a different + Transaction-Manager (Multi-TM scenario) the routine enters a loop + where it temporarily switches to additional KTM objects stored in + v14 / v16. + +3. PRIOR TO THE PATCH the additional KTM object was used as follows: + • v14/v16 is taken straight from a global list. + • TmpAcquireTxTableTransactionManager(v14) is called, which + only acquires an internal lock – it does not increase the + reference count. + • The pointer is later accessed again (e.g. v11[32]) *after* + the lock has been released and even after the function has + restarted the loop. + • No ObfReferenceObject was taken for v14/v16, so another + thread closing the KTM handle could free the memory between + release and later access. + +4. When the freed memory is dereferenced the kernel operates on + attacker-controlled data, enabling privilege escalation. + +5. The bug can be triggered reliably by: + a. Creating two Transaction-Managers. + b. Opening/closing one KTM handle in a tight loop on a helper + thread. + c. From the attacker thread, creating an enlistment that + forces the Multi-TM path (specific notification mask and + enlist-sup options). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – no reference is taken on v14/v16 +if (v14) { + TmpAcquireTxTableTransactionManager(v14); // lock only + v12 = (_DWORD *)v14; + i = 0; + if (*(_DWORD *)(v14 + 0x40) == 5) { + inserted = STATUS_OBJECT_TYPE_MISMATCH; // bail out + goto cleanup; + } +} +... +if (v12 && (v11[32] & 0x10) != 0) // <== UAF if v11 freed + Transaction->Flags |= 0x10000; +``` +```c +// AFTER – extra lifetime management +if (v16) { + ObfReferenceObject(v16); // NEW + TmpAcquireTxTableTransactionManager((__int64)v16); + v11 = v16; + v35 = 0; + if (v16[16] == 5) + goto error; +} +... +TmpReleaseTxTableTransactionManager((__int64)v11); +if (FeatureFlag) + ObfDereferenceObject(v11); // NEW matched deref +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode calls NtCreateEnlistment with crafted flags. +2. KtmRm creates Resource Manager and enters TmpInitializeEnlistment. +3. Function switches to a secondary KTM without taking a reference. +4. Attacker thread closes the KTM handle -> object is freed. +5. Primary thread resumes, accesses v11/v16 -> Use-After-Free. +6. Corrupted kernel heap structures allow arbitrary code execution in + kernel context. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker, already running with the ability to open/close +Transaction-Manager handles (no special privilege is required for +NtCreateEnlistment). Exploit runs two cooperating threads to win the +race and elevate to SYSTEM. + + +Patch Description +-------------------------------------------------------------------- +1. Replaced TmpIsNotificationMaskValid() by strict in-line bitmask + checks, limiting the code paths that reach the vulnerable multi-TM + logic. +2. Added ObfReferenceObject()/ObfDereferenceObject() around every + temporary KTM pointer (v16) whenever it is acquired/released. +3. Added new local flags (v24, v34) to guarantee that every mutex and + reference is released on *all* exit paths. +4. Mirrored changes in the _fin variant so both hot and cold builds are + protected. + + +Security Impact +-------------------------------------------------------------------- +An unprivileged local user could trigger a race causing the kernel to +use a freed Transaction Manager object, leading to arbitrary code +execution in kernel mode and full privilege escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The additional object referencing guarantees that the KTM object’s +reference count remains non-zero while it is being used. Releasing the +reference on every failure/exit path removes the dangling pointer. The +inline mask validation further reduces reachable states. Together these +changes fully eliminate the Use-After-Free condition described above. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53141_afd.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53141_afd.sys.txt new file mode 100644 index 0000000..ff405d9 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53141_afd.sys.txt @@ -0,0 +1,126 @@ +{'date': 1755089272.0840704, 'patch_store_uid': '230e7c66-244b-4041-b166-2157fff153a1', 'cve': 'CVE-2025-53141', 'file': 'afd.sys', 'kb': 'KB5063878', 'change_count': 8, 'confidence': 0.22} +-------------------------------------------------------------------- +CVE-2025-53141 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel – afd.sys (Ancillary Function Driver for WinSock) + + +Vulnerability Class +-------------------------------------------------------------------- +NULL-pointer / NULL function-pointer dereference leading to +Elevation-of-Privilege (CWE-476) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The AFD endpoint structure (typ. struct _AFD_ENDPOINT) contains two +callback-tables that are supplied by the underlying transport layer +(TL) driver: + + offset +0x1E0 (0x480) -> TL handle + + offset +0x1E8 (0x488) -> pointer to a table of close / IO + completion routines + +During the AFD_UNBIND ioctl (handled by AfdUnBindSocket) the driver +has to tear-down an eventually created dual-stack TL endpoint. When +the socket was opened with the AFD_ENDPOINT_FLAG_TL_PROVIDER (bit +0x200) set, the legacy code performed the following sequence: + + 1. v21 = *(endpoint + 0x480); // TL handle + 2. v19 = *(endpoint + 0x488); // **function table** + 3. (*v19)(v21, params); // unconditional call + +If the transport layer initialisation had never completed, field +0x488 stayed NULL. Step 3 therefore dereferenced address 0x0, causing +an exception in kernel context. Because user-mode code can reach this +path with only a socket handle, the crash is fully triggerable from a +least-privileged account. Mapping user controlled data to address 0x0 +(nowadays restricted but still possible on some systems / configs) +turns the crash into a controlled function-pointer call and therefore +an elevation of privilege. + +Conditions that lead to a NULL callback pointer: +• Create IPv6 socket, call IOCTL_AFD_BIND/CONNECT so that the IPv4 + “shadow” TL endpoint is requested but fails early. +• The 0x200 flag is set, but the TL provider never returns its + dispatch table – endpoint->TlDispatch becomes NULL. +• Invoke IOCTL_AFD_UNBIND. + +Because AfdUnBindSocket executed the callback unconditionally, the +kernel dereferenced NULL every time the above sequence was followed. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// afd!AfdUnBindSocket (before) +... +v19 = *(unsigned int (__fastcall ***)(__int64, __int64 *))(ep + 0x488); +... +if ( (*v19)(v18, v32) == STATUS_PENDING ) // <== v19 may be NULL + KeWaitForSingleObject(...); +``` +```c +// afd!AfdUnBindSocket (after) +... +v22 = *(unsigned int (__fastcall ***)(__int64, __int64 *))(ep + 0x488); +... +if ( !FeatureGuard || v22 ) // new null-check +{ + KeResetEvent(...); + args[0] = AfdSynchronousTlCloseRequestComplete; + args[1] = (INT64)&Evt; + if ( (*v22)(v21, args) == STATUS_PENDING ) + KeWaitForSingleObject(...); +} +else // pointer was NULL +{ + WPP_SF_(40, TraceGuid); // trace & skip call +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process + └─ ioctl( AFD_UNBIND, sockHandle, bufWithSockaddr ) +2. afd.sys : AfdUnBindSocket + └─ enters path for TL-provider sockets (flag 0x200) +3. Endpoint->TlDispatch is still NULL +4. NULL call *TlDispatch->Close() ➜ kernel exception / EoP + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker with the ability to create and control +WinSock sockets. No special privileges are required; any user can +open a socket and issue the crafted IOCTL sequence. + + +Patch Description +-------------------------------------------------------------------- +The patch adds explicit validation before the indirect call: +• reads the function-pointer table into a local variable (v22) +• executes the close routine only if v22 is non-NULL or if the new + feature-guard is disabled +• otherwise emits a trace message and skips the call +Similar defensive checks were inserted in other code-paths that used +endpoint callback pointers (AfdGetAddress, AfdQueryHandles, etc.). + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a user-controlled NULL function-pointer dereference +allowed local DoS and, on systems where a NULL page can be mapped +(e.g. through a driver bug or incompatible mitigation), arbitrary code +execution in kernel mode, resulting in full privilege escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable call site is now guarded by a concrete NULL test, +ensuring that no indirect call is issued when the callback table is +absent. No additional paths to the same function pointer were found +in the updated binary, making the fix comprehensive for this flaw. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53142_bfs.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53142_bfs.sys.txt new file mode 100644 index 0000000..a2fbe15 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53142_bfs.sys.txt @@ -0,0 +1,131 @@ +{'kb': 'KB5063878', 'date': 1755086334.7790911, 'file': 'bfs.sys', 'change_count': 2, 'patch_store_uid': '551f5526-61e2-4a1e-9bf8-0c09a9d5426b', 'cve': 'CVE-2025-53142', 'confidence': 0.05} +-------------------------------------------------------------------- +CVE-2025-53142 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Brokering File System (bfs.sys) – policy-management code +that lives in the kernel driver and is reached through routine +BfsGetPolicyEntry. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. BfsGetPolicyEntry() first attempts to locate a policy entry in a + hash table protected by a push-lock. When the entry does not yet + exist, the routine releases the lock and calls + BfsInsertPolicyEntry() in order to allocate and initialise a new + BFS_POLICY_ENTRY structure; the address of that structure is + returned through the out-parameter P. + +2. BfsInsertPolicyEntry() returns STATUS_SUCCESS on success, or a + negative NTSTATUS when allocation/initialisation fails. When the + function fails it frees the partially-built entry before + returning, but it still leaves the now-stale pointer value in the + caller-supplied variable P. + +3. In the original (vulnerable) implementation the caller executes + + if (P) BfsDereferencePolicyEntryEx(P); + + on every error path, assuming that P is still a valid, referenced + object. Because the entry has already been freed by + BfsInsertPolicyEntry(), this second dereference operates on freed + pool memory. The result is a classic UAF/double-free that corrupts + the kernel pool and yields arbitrary kernel-mode read/write + primitives. + +4. An attacker running in a normal user context can reliably force the + failing path by exhausting PagedPool, by providing extremely large + SIDs, or by racing two threads so that the second thread receives + STATUS_OBJECT_NAME_EXISTS from BfsInsertPolicyEntry(). After the + entry is freed, the attacker can re-allocate the same memory with + controlled data and wait for the spurious dereference to gain code + execution in kernel mode. + +5. The issue is entirely inside bfs.sys and does not require admin + privileges – only the ability to create files that make the filter + driver execute the policy lookup path. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – error path unconditionally dereferences freed object +inserted = BfsInsertPolicyEntry(..., &P); +... +if (inserted < 0) { + ... + if (P) + BfsDereferencePolicyEntryEx(P); // <== UAF + return inserted; +} + +// after patch – early exit, no dereference after failure +inserted = BfsInsertPolicyEntry(..., &P); +if (Feature_IsEnabled() && inserted < 0) + return inserted; // safe +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User provokes a call to BfsPreCreateOperation() -> BfsCheckAndApply + Policy() -> BfsGetPolicyEntry(). +2. Inside BfsGetPolicyEntry(): + a. Entry not found, lock is released. + b. BfsInsertPolicyEntry() fails (e.g., STATUS_INSUFFICIENT_RESOURCES + or STATUS_OBJECT_NAME_EXISTS) and frees the newly allocated + entry. + c. Function returns to BfsGetPolicyEntry() with P pointing at freed + memory and status < 0. + d. Original code calls BfsDereferencePolicyEntryEx(P) – UAF. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By exhausting pool or racing duplicate +policy insertions the attacker triggers the failure path and forces the +driver to dereference attacker-controlled, freed memory from kernel +context, leading to elevation of privilege. + + +Patch Description +-------------------------------------------------------------------- +The update makes two defensive changes in BfsGetPolicyEntry(): +1. Reverses the success test so that the routine reaches the allocation + only when strictly necessary. +2. Most importantly, after BfsInsertPolicyEntry() returns a failure + status (<0) the function now exits immediately (when the new feature + gate is enabled) and therefore never touches the stale pointer. + This removes the double-dereference of an already-freed object. + +No structural changes are made to the underlying POLICY_ENTRY – only +control-flow is altered to prevent use of freed memory. + + +Security Impact +-------------------------------------------------------------------- +A local attacker can execute arbitrary code in kernel mode, thereby +achieving a full elevation of privilege and escaping any sandbox or +user-mode restrictions. System integrity and confidentiality are +compromised. + + +Fix Effectiveness +-------------------------------------------------------------------- +The early-return added after the failed insert ensures that the pointer +is not reused after it has been freed, eliminating the immediate UAF +window. No remaining paths in BfsGetPolicyEntry() dereference P when +status < 0, so the specific vulnerability is resolved. Effectiveness +is contingent on the controlling feature flag being enabled by default; +this appears to be the case in supported Windows builds. Full +regression-testing would still be required to confirm that no parallel +error paths re-introduce the bug elsewhere. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53143_mqqm.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53143_mqqm.dll.txt new file mode 100644 index 0000000..1fc840e --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53143_mqqm.dll.txt @@ -0,0 +1,128 @@ +{'confidence': 0.18, 'cve': 'CVE-2025-53143', 'change_count': 11, 'kb': 'KB5063878', 'date': 1755089406.5024087, 'file': 'mqqm.dll', 'patch_store_uid': '2d4fad2a-6929-40c4-8e2b-8117e27bb0cc'} +-------------------------------------------------------------------- +CVE-2025-53143 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Message Queuing (MSMQ) +User-mode service binary: mqqm.dll +Functions affected: + • CMessageTransport::DeliverPacket + • CMessageTransport::GetPacketForSendingSucceeded + • RPC_INT_XACT_HANDLE_rundown / QMDo*Transaction helpers + • PCTX_OPENREMOTE_HANDLE_TYPE_rundown + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion – Access of resource using incompatible type (CWE-843). +An EXOVERLAPPED pointer returned by I/O completion is interpreted as a +CMessageTransport object that never actually owns that memory. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. When MSMQ sends data it builds an extended OVERLAPPED structure and + submits it with WSASend/WriteFile. + +2. Before the patch the send routine allocates only the EXOVERLAPPED + object itself (0x40 bytes) on the heap: + v5 = MmAllocate(0x40); + /* fill a few fields and submit */ + +3. Upon completion the runtime calls + CMessageTransport::GetPacketForSendingSucceeded(EXOVERLAPPED *ov). + The routine tries to recover the owning transport object with + owner = (char*)ov – 0x1E8; // 0x1E8 = 488 dec + + This assumes that the EXOVERLAPPED is *embedded* inside a much + larger CMessageTransport object at offset 0x1E8. In reality the + pointer handed back by the I/O subsystem is the heap buffer that was + just allocated in DeliverPacket, so the arithmetic produces a + pointer 488 bytes *before* the real allocation. + +4. The resulting mis-typed pointer is immediately dereferenced for: + • virtual-method dispatch (function-pointer call) + • reference-count updates + • critical-section access + Because the memory region is outside the allocated buffer the + attacker can influence its contents through heap shaping or payload + data, ultimately controlling the function pointer and achieving + remote code execution in the MSMQ service (runs under the service + account). + +5. The same incorrect assumption affects the error path + GetPacketForSendingFailed (not shown) and other helpers that use the + same cast pattern. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v5 = MmAllocate(0x40ui64); // allocate only EXOVERLAPPED +... +// callback +char *owner = (char*)ov - 488; // treat as CMessageTransport +... +(*(void (__fastcall **)(_QWORD, CQmPacket *))(**((_QWORD **)owner+10)+8))(...); +``` +```c +// after +void *buf = MmAllocate(0x40ui64); +CSendOv *ov = CSendOv::CSendOv(buf, this, &reqBuf); // builds + // full wrapper +// callback logic unchanged; subtraction now lands inside CSendOv +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker causes MSMQ to establish an outgoing session. +2. CMessageTransport::DeliverPacket allocates 0x40-byte OVERLAPPED and + submits async send. +3. Completion arrives -> GetPacketForSendingSucceeded is invoked with + pointer to that buffer. +4. Pointer arithmetic under-flows, creating forged CMessageTransport*. +5. Forged object’s vtable / fields are used, leading to controlled + code execution. + + +Attack Vector +-------------------------------------------------------------------- +A network client with permission to interact with the remote queue sends +crafted traffic that forces the service to send a packet back. When the +packet completes, the malformed bookkeeping causes type confusion. No +local privileges are required; exploit works over the network via the +MSMQ protocol (TCP port 1801). + + +Patch Description +-------------------------------------------------------------------- +1. Introduced class CSendOv whose first member is the EXOVERLAPPED; the + object size is at least 0x1E8+0x40, so `ov-0x1E8` now points to the + CSendOv header that deliberately carries a back-pointer to the owning + CMessageTransport. +2. DeliverPacket now calls the CSendOv constructor instead of manually + zeroing memory, guaranteeing correct layout and initialisation. +3. Additional state/feature checks (IsDeliveryAllowed, Validate*() ) + were added to refuse invalid objects early but are secondary. + + +Security Impact +-------------------------------------------------------------------- +Before the fix an authenticated MSMQ peer could trigger a function- +pointer dereference through heap-based type confusion, yielding +arbitrary code execution in the MSMQ service process (RCE, same +privilege as mqsvc.exe, typically NT AUTHORITY\NETWORK SERVICE). + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code guarantees that the EXOVERLAPPED handed to the I/O +API is embedded in a correctly-sized CSendOv wrapper, eliminating the +invalid pointer calculation and thereby removing the primitive needed +for type confusion. No residual paths to the old layout were observed +in the provided diff, so the remediation appears complete. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53144_mqqm.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53144_mqqm.dll.txt new file mode 100644 index 0000000..7a03e1f --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53144_mqqm.dll.txt @@ -0,0 +1,158 @@ +{'confidence': 0.23, 'kb': 'KB5063878', 'change_count': 11, 'patch_store_uid': '2d4fad2a-6929-40c4-8e2b-8117e27bb0cc', 'file': 'mqqm.dll', 'date': 1755086343.31281, 'cve': 'CVE-2025-53144'} +-------------------------------------------------------------------- +CVE-2025-53144 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Message Queuing (MSMQ) service – mqqm.dll. Vulnerable RPC +entry points include + • PCTX_OPENREMOTE_HANDLE_TYPE_rundown + • QMDoAbortTransaction / QMDoCommitTransaction + • RPC_INT_XACT_HANDLE_rundown + • CMessageTransport::GetPacketForSendingSucceeded +and the helper routines they invoke. + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / access of resource using incompatible type +(CWE-843). The code blindly interprets an un-trusted pointer coming +from an RPC context handle as a valid internal object (CTransaction, +CTX_OPENREMOTE_BASE, CMessageTransport, …). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. MSMQ exposes several RPC methods that return opaque context + handles to a remote caller (e.g. QMOpenRemoteQueue, remote + transaction APIs). The context handle is nothing more than a raw + pointer that later gets passed back to the server during Abort, + Commit or rundown operations. + +2. Prior to the patch the rundown / control paths trusted this raw + value and immediately dereferenced it: + PCTX_OPENREMOTE_HANDLE_TYPE_rundown(CReference *this) + RPC_INT_XACT_HANDLE_rundown(CTransaction *this) + QMDoAbortTransaction(CTransaction **a1) + QMDoCommitTransaction(CTransaction **a1) + + Typical first access: + if (*((_DWORD*)this + 4)) { … } + + No attempt was made to verify that + • the pointer is non-NULL, and + • it really refers to an allocated instance of the expected + class that is still owned by the current Resource Manager / + Context Map. + +3. Because the value is fully controlled by an RPC client, an + attacker can supply an address that: + a. Points to attacker-mapped user memory in the service process + (possible through other bugs, shared sections, etc.), or + b. Aliases a different internal object whose layout is + incompatible with the expected type. + + When MSMQ reads or writes object fields (counters, VFT pointer, + list links, critical-section objects, …) memory corruption ensues + – ultimately enabling arbitrary code execution in the MSMQ + service (running as NT AUTHORITY\SYSTEM). + +4. Similar trust problem existed in + CMessageTransport::GetPacketForSendingSucceeded. The function + used the EXOVERLAPPED back-pointer (a1 – 488) without confirming + it still represents a live CMessageTransport instance. The patch + added IsDeliveryAllowed / PrepareDelivery gating but, more + importantly, keeps a consistent pattern: *validate before use*. + +Structures / parameters involved +• CTransaction – 0x30+ bytes, state in dword[2] +• CTX_OPENREMOTE_BASE / CTX_OPENREMOTE_HANDLE_TYPE – ~0xB0 bytes, + ref-count at +8, map-index at +104. +• CMessageTransport – EXOVERLAPPED embedded at offset 0. +• Global maps: qword_180148A10 (ContextMap), etc. + +None of these were checked before, leading directly to type +confusion. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +void PCTX_OPENREMOTE_HANDLE_TYPE_rundown(CReference *this) { + if (*((_DWORD*)this + 4)) { + … // uses fields of attacker pointer + } else { + if (!_InterlockedCompareExchange((int*)this + 27,2,0)) + DeleteFromContextMap(this); + CReference::Release(this); // virtual call via vftbl + } +} + +// After +if (!this) { … return; } +if (ValidateContext(this)) { + … // original logic +} else { + LogMsgHR(STATUS_INVALID_HANDLE,…); + return; // no dereference +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker connects to MSMQ RPC interface (TCP port 1801 by default). +2. Calls any API that expects a context handle (e.g. QMDoCommit* + or remote queue rundown) and supplies a crafted 64-bit value. +3. Service invokes corresponding *rundown* or control procedure when + the handle is used. +4. Pre-patch code treats the value as a valid object, dereferences + invalid memory, corrupts heap / vtable, and executes arbitrary + code. + + +Attack Vector +-------------------------------------------------------------------- +Network – authenticated (or otherwise authorised) RPC client sends a +malformed context handle over MSMQ’s private RPC protocol. No local +code execution on the server is needed. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced ValidateContext(), ValidateTransaction() and + CMessageTransport::IsDeliveryAllowed / PrepareDelivery gates. + These functions make sure that the supplied pointer exists in the + appropriate global ContextMap / ResourceManager list and matches + the expected object type. +2. Added explicit NULL checks prior to dereference. +3. If validation fails the code logs an error and returns + MQ_ERROR_INVALID_HANDLE (0xC00E000F / ‑1072824313) instead of + using the pointer. +4. Refactored GetPacketForSendingSucceeded logic to avoid using a + potentially dead CMessageTransport instance and to bail out early + on invalid state. +5. Mechanical changes: feature-flag plumbing, trace-guid updates, + moved critical section from stru_180147A50 to stru_180148A50. + + +Security Impact +-------------------------------------------------------------------- +Without these checks a remote attacker could craft a bogus context +handle, trigger out-of-bounds reads/writes or virtual-function calls +on a forged vtable, and ultimately achieve remote code execution in +the MSMQ service process (SYSTEM integrity). Denial of service is +also trivial. + + +Fix Effectiveness +-------------------------------------------------------------------- +All affected entry points now validate the caller-supplied handle +before any field access or virtual call. If validation fails, the +function aborts with an error and never touches the pointer. No +further type confusion path is visible in the patched code provided. +Assuming Validate* functions are trustworthy, the patch fully +mitigates the vulnerability. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53145_mqqm.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53145_mqqm.dll.txt new file mode 100644 index 0000000..cfef115 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53145_mqqm.dll.txt @@ -0,0 +1,140 @@ +{'confidence': 0.27, 'patch_store_uid': '2d4fad2a-6929-40c4-8e2b-8117e27bb0cc', 'file': 'mqqm.dll', 'cve': 'CVE-2025-53145', 'date': 1755089359.7205603, 'change_count': 11, 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-53145 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Message Queuing (MSMQ) user-mode service – mqqm.dll + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / access of resource using incompatible type (CWE-843) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Packet delivery uses an async-IO helper object called CSendOv. In + CMessageTransport::DeliverPacket the pre-patch code allocates 0x40 + bytes with MmAllocate() and then *manually* populates the buffer + with function pointers and two reference counters: + v5[4] = SendSucceeded + v5[5] = SendFailed + *(OWORD*)v5 = 0 + *((OWORD*)v5+1) = 0 + v5[6] = SafeAddRef(this) + *(QWORD*)(v6+56) = SafeAddRef(v7) + No constructor is invoked, so the buffer is only + *assumed* to be a valid CSendOv. A crafted packet that triggers a + different code path (e.g. IO completion, rundown, commit/abort + helpers) can therefore treat the same memory as a different class + and call virtual functions at uncontrolled offsets. This is the + classic type-confusion pattern – an object is used through two + incompatible layouts. + +2. Additional entry points (RPC_INT_XACT_HANDLE_rundown, + QMDoCommitTransaction, QMDoAbortTransaction, and + PCTX_OPENREMOTE_HANDLE_TYPE_rundown) were missing sanity checks for + caller-supplied context-handle pointers. The pointers were blindly + dereferenced, allowing an attacker to pass a forged 64-bit value + that is later interpreted as a CTransaction/CReference object. The + fake object again leads to virtual-function-pointer confusion and + code execution in the msmq service process. + +3. The patched code eliminates both issues: + • A real CSendOv constructor + (CSendOv::CSendOv) is now invoked, guaranteeing that the memory + really contains a valid CSendOv layout. + • Every RPC-exposed rundown or transaction helper now calls + CResourceManager::ValidateTransaction() or ValidateContext() + before touching the pointer. If validation fails the function + logs and bails out. + +The root cause is therefore an implicit trust in the type of heap +buffers and RPC context handles combined with manual object crafting, +which lets remote, authenticated users confuse MSMQ into executing code +through forged objects. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// 1. Manual object creation (before) +v5 = MmAllocate(0x40ui64); +if (v5) { + v5[4] = CMessageTransport::SendSucceeded; // virtuals + v5[5] = CMessageTransport::SendFailed; + *(OWORD*)v5 = 0; // raw zeroing + *((OWORD*)v5 + 1) = 0; + v5[6] = SafeAddRef(this); + *(QWORD*)(v6 + 56) = SafeAddRef(v7); +} + +// 1. Correct object creation (after) +v5 = MmAllocate(0x40ui64); +if (v5) + v8 = CMessageTransport::CSendOv::CSendOv(...,&v18); // real ctor +``` + +```c +// 2. Pointer validation added to RPC rundown (after) +if (wil::FeatureEnabled && !CResourceManager::ValidateTransaction(v4,this)) { + WPP_SF_sq(...); + LogMsgHR(-1072824313,L"xactrm",2113); + return; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client sends crafted MSMQ packet / issues RPC with forged + context handle. +2. Service thread enters CMessageTransport::DeliverPacket or one of the + *_rundown helpers. +3. Pre-patch: buffer is cast to CSendOv or CTransaction with no + validation; virtual functions are invoked. +4. Attacker-controlled memory is executed, yielding RCE in msmq.exe + (running as service account / LocalSystem by default). + + +Attack Vector +-------------------------------------------------------------------- +Authenticated network attacker (or compromised client machine) sends a +specially crafted message to an MSMQ queue or calls the MSMQ RPC +interface, supplying a malicious context handle that forces the service +to treat attacker-controlled memory as an internal C++ object. + + +Patch Description +-------------------------------------------------------------------- +1. Replaces hand-crafted object initialisation with a call to + CMessageTransport::CSendOv::CSendOv, ensuring correct vtable and + member layout. +2. Introduces ValidateContext() and + CResourceManager::ValidateTransaction() checks in every RPC rundown + and transaction API before the pointer is dereferenced. +3. Minor logic flips in GetPacketForSendingSucceeded to avoid + processing of not-yet-validated packets. +4. Feature gates (wil::FeatureImpl) are used so that validation can be + enabled through a flighting flag without code changes. + + +Security Impact +-------------------------------------------------------------------- +Without the patch an attacker with network access to MSMQ could craft a +message or context handle that results in arbitrary function-pointer +invocation inside the service, leading to remote code execution under +SERVICE / LocalSystem privileges. This compromises the entire host. + + +Fix Effectiveness +-------------------------------------------------------------------- +The constructor call removes the underlying type confusion in +DeliverPacket. Added ValidateContext / ValidateTransaction checks stop +forged handles early. No remaining unsafe casts were observed in the +patched diff, so the fix appears complete for the identified paths. +However, full assurance would require a code-base audit for other +manual object allocations and for callers executed when the new Wil +feature flag is disabled. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53147_afd.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53147_afd.sys.txt new file mode 100644 index 0000000..8f2e7d6 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53147_afd.sys.txt @@ -0,0 +1,127 @@ +{'confidence': 0.15, 'file': 'afd.sys', 'patch_store_uid': '230e7c66-244b-4041-b166-2157fff153a1', 'change_count': 8, 'cve': 'CVE-2025-53147', 'kb': 'KB5063878', 'date': 1755089294.5137217} +-------------------------------------------------------------------- +CVE-2025-53147 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) – code paths +handling socket bind / un-bind and various query helpers +(AfdUnBindSocket, AfdGetAddress, AfdQueryHandles, AfdExtractAfdSendMsgInfo, +AfdSetConnectData, AfdBind). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (time-of-check / time-of-use race on a freed +pool buffer). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Every AFD endpoint keeps a pointer to the buffer that describes the + local address bound to the socket: + +0x0EC LocalAddressLength (DWORD at offset 0xEC / 236) + +0x0F0 LocalAddress (PVOID at offset 0xF0 / 240) + (layout name taken from public symbols). + +2. AfdUnBindSocket frees that buffer when a user issues the *AFD_IOCTL + Unbind* request: + a. The code reaches the *else* branch (no transport-level endpoint). + b. It calls ExEnterCriticalRegionAndAcquireResourceExclusive on the + global AFD resource **only**, then immediately: + ExFreePoolWithTag( Endpoint->LocalAddress, 0x41646C6C ); + Endpoint->LocalAddress = NULL; + Endpoint->LocalAddressLength = 0; + c. The global resource is released, and the request returns. + No per-endpoint spin-lock is taken while freeing the buffer. + +3. Several helper paths (GetAddress, QueryHandles, ExtractSendMsgInfo, + SetConnectData, …) copy that same buffer back to user or to the + transport **without any synchronisation** in the original code: + Length = *(DWORD *)(Ep + 0xEC); + memcpy( dst, *(PVOID *)(Ep + 0xF0), Length ); + +4. A racing sequence therefore exists: + • Thread A : calls IOCTL_AFD_UNBIND → frees LocalAddress. + • Thread B : in parallel issues IOCTL_AFD_GET_ADDRESS (or others) and + dereferences the stale pointer after the pool block has + been freed / reused. + The stale read occurs in arbitrary IRQL and the freed pool block is + fully attacker-controlled after being re-allocated, leading to kernel + memory disclosure or, via crafted re-allocation, elevation of + privilege. + +5. The vulnerability is reachable from user mode by any account that + owns a socket handle – no additional privileges are required. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// AfdUnBindSocket (before) +ExEnterCriticalRegionAndAcquireResourceExclusive(AfdGlobalData); +ExFreePoolWithTag(*(PVOID *)(Ep + 0xF0), 'AFdL'); +*(PVOID *)(Ep + 0xF0) = NULL; // LocalAddress +*(DWORD *)(Ep + 0xEC) = 0; // Length +ExReleaseResourceAndLeaveCriticalRegion(AfdGlobalData); +``` +```c +// AfdExtractAfdSendMsgInfo (before) +Size = *(DWORD *)(Ep + 0xEC); +memmove(UserDst, *(PVOID *)(Ep + 0xF0), Size); // no lock, UAF! +``` +```c +// AfdExtractAfdSendMsgInfo (after) +KeAcquireInStackQueuedSpinLock((PKSPIN_LOCK)(Ep + 0x38), &Lock); +Size = *(DWORD *)(Ep + 0xEC); +Src = *(PVOID *)(Ep + 0xF0); +KeReleaseInStackQueuedSpinLock(&Lock); +memmove(UserDst, Src, Size); // safe +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. T1 : socket-owner calls DeviceIoControl(…, IOCTL_AFD_UNBIND, …) + → AfdDispatch → AfdUnBindSocket → frees LocalAddress. +2. T2 : concurrently issues IOCTL_AFD_GET_ADDRESS / AFD_SENDMSG / + AFD_QUERY_HANDLES … +3. Helper routine copies from freed pool block, operating in kernel + context → use-after-free and controlled memory access. + +Attack Vector +-------------------------------------------------------------------- +Local user possessing a socket handle starts two threads: one continually +unbinds the socket while the second performs any of the affected AFD +ioctls that read the local address. By grooming the pool the attacker +can cause privilege escalation or information disclosure. + +Patch Description +-------------------------------------------------------------------- +Microsoft rewrote all participants of the race: +1. Reader paths (GetAddress, ExtractAfdSendMsgInfo, SetConnectData, + QueryHandles, …) now acquire the endpoint’s internal spin-lock + (offset +0x38) before touching LocalAddress/Length and re-validate the + data after the lock is held. +2. AfdUnBindSocket’s fast path was hardened – extra state checks were + inserted and, if a transport end-point still exists, the code now + refuses to unbind (returns STATUS_INVALID_DEVICE_STATE) or performs + a safe two-phase close. +3. Multiple *AfdPreventUnbind* / *AfdReallowUnbind* and feature-flag + gates were added so that unbind cannot proceed while another thread + holds a reference obtained by the reader helpers. +4. Defensive copies and length checks were added for buffers ≤0x100 + bytes and for CMSG handling. + +Security Impact +-------------------------------------------------------------------- +Before the patch any local user could obtain an arbitrary kernel-pool +use-after-free, leading to EoP (SYSTEM) or information disclosure. The +race is reliable because both sides execute in arbitrary process +context, no timing constraints exist (standard pool). + +Fix Effectiveness +-------------------------------------------------------------------- +Synchronising every read of LocalAddress with the same spin-lock that is +implicitly held (or, for the slow path, by blocking unbind) removes the +TOCTOU window; the pointer is now either alive for the whole critical +section or unbind is denied. No obvious paths remain where the endpoint +structure is accessed without the lock, therefore the fix is considered +complete. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53149_ksthunk.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53149_ksthunk.sys.txt new file mode 100644 index 0000000..a098028 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53149_ksthunk.sys.txt @@ -0,0 +1,166 @@ +{'kb': 'KB5063878', 'patch_store_uid': 'cfd25660-cbce-42b2-816e-22a3968aaf50', 'cve': 'CVE-2025-53149', 'date': 1755086291.2022102, 'change_count': 3, 'confidence': 0.36, 'file': 'ksthunk.sys'} +-------------------------------------------------------------------- +CVE-2025-53149 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Kernel Streaming WOW-Thunk Service Driver +(ksthunk.sys), specifically the automation/streaming helper +routines: + • CKSAutomationThunk::HandleArrayProperty() + • CKSThunkDevice::CheckIrpForStackAdjustmentNative() + • CKSThunkPin::ThunkStreamingIrp() + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / improper size validation (CWE-122) +leading to kernel memory corruption and local elevation of +privilege. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. HandleArrayProperty(GET path) + • For property queries where Flags bit 1 is clear + (a2->Flags & 2 == 0) the routine receives a variable-length + array from the underlying KS filter. + • BytesReturned (length provided by the lower driver) is used + verbatim to allocate PoolWithTag. + • After the second KsSynchronousIoControlDevice() call the + buffer layout is assumed to be: + DWORD Count (PoolWithTag[0]) + DWORD Padding + DWORD Data[Count] (PoolWithTag+2 … ) + • The driver immediately performs the copy + for i in 0..Count-1 + *(a3+1+i) = PoolWithTag[2+2*i] + without checking that: + – Count * sizeof(DWORD) is actually present inside + BytesReturned; or + – the caller-supplied output buffer length + (CurrentStackLocation->Parameters.Read.Length) is large + enough for Count+1 DWORDS. + • A malicious user mode client can supply a small output buffer + length yet make the lower filter return an arbitrarily large + Count value. The loop therefore writes past the end of the + caller’s buffer and corrupts kernel heap memory because the + copy is performed with a kernel pointer (MmProbeForWrite has + already succeeded). + +2. CheckIrpForStackAdjustmentNative & ThunkStreamingIrp + • Similar patterns existed: user-controlled size fields taken + from CREATE_PIPE parameters or KSSTREAM_HEADER structures are + trusted when allocating pool or building MDLs. + • Copies were performed with memmove() / RtlCopyFromUser() with + no secondary range checks, allowing integer truncation or + length mismatches to overflow the destination allocations. + +3. Patch corrections + • Introduces precise integer-overflow checks + new variables v20 / v21 etc. ensure + (Count + 1) * 4 < 2^32 and no wrap-around. + • Verifies that Read.Length ≥ 4*(Count+1) before copying. + • On failure the code now returns STATUS_INVALID_PARAMETER + (-0x7FF8F009) and logs with RtlLogUnexpectedCodepath(). + • Replaces blind memmove with RtlCopyVolatileMemory() to remove + stale-data TOCTOU windows and adds feature-gated paths for + hardened probing. + +Structures / parameters affected + KSARRAY property blob : + DWORD ElementCount; + DWORD Elements[...]; + KSSTREAM_HEADER : size / FrameExtent / DataUsed / Flags + NAMED_PIPE_CREATE_PARAMETERS inside IRP_MJ_CREATE ioctl path. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +*a3 = *v15; // v15 == PoolWithTag +if (*(_QWORD *)v15) { + do { + ++v5; + *v4 = *v19; // v4 = a3+1, v19 = PoolWithTag+2 + v19 += 2; + ++v4; + } while ((unsigned __int64)v5 < *(_QWORD *)v15); +} + +// after (excerpt) +v20 = *PoolWithTag + 1; +if ((uint)v20 < *PoolWithTag || // underflow + 4*v20 > 0xFFFFFFFF || // 32-bit limit + CurrentStackLocation->Parameters.Read.Length < 4*v20) +{ + RtlLogUnexpectedCodepath(...); + v17 = STATUS_INVALID_PARAMETER; + goto cleanup; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens a KS filter via ksproxy and obtains a handle + to ksthunk.sys. +2. Sends IOCTL_KS_PROPERTY with KSPROPERTY_TYPE_GET and crafted + property id that is handled by HandleArrayProperty(). +3. Lower filter returns a large element count; user supplies a + small output buffer. +4. Driver copies Count*4 bytes past the end of its pool-allocated + buffer, corrupting pool metadata. +5. Corrupted heap objects allow execution of arbitrary kernel + code, yielding SYSTEM privileges. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running in a 32-bit user-mode +process on a 64-bit Windows system. Requires only the ability to +issue DeviceIoControl calls to the publicly accessible KS device +interface (no special privileges). No user-interaction needed. + + +Patch Description +-------------------------------------------------------------------- +• Added strict integer arithmetic checks (overflow & truncation) + on element counts, sizes, and IRP buffer lengths. +• Added CurrentStackLocation->Parameters.Read.Length comparison + before copying results back to caller. +• Replaced memmove/ProbeForRead paths with + RtlCopyVolatileMemory / additional ProbeForRead/Write to avoid + TOCTOU. +• Hardened metadata handling in + CheckIrpForStackAdjustmentNative() and ThunkStreamingIrp(), + including new IoAllocateMdl() guards. +• Failure paths now free allocations and return proper NTSTATUS + errors instead of silently continuing. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a malformed KS property/stream request allowed +arbitrary heap overwrite in the kernel’s non-paged pool. An +attacker could craft the overwrite to: + – Modify function pointers or OBJECT_HEADER fields + – Achieve arbitrary kernel-mode code execution + – Escalate from normal user privileges to SYSTEM + +No mitigations blocked the primitive because the copy occurred +entirely in kernel context after ProbeForWrite succeeded. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added arithmetic checks guarantee that the driver never copies +more than the caller-supplied buffer length and that all size +calculations stay within 32-bit limits; loops now operate only on +validated counts. Pool freeing logic is executed on all error +exits. No residual uncontrolled copy paths were found in the +modified routines, making the patch effective against the reported +heap overflow. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53151_ntoskrnl.exe.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53151_ntoskrnl.exe.txt new file mode 100644 index 0000000..ede3830 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53151_ntoskrnl.exe.txt @@ -0,0 +1,141 @@ +{'file': 'ntoskrnl.exe', 'cve': 'CVE-2025-53151', 'kb': 'KB5063878', 'date': 1755089720.1652136, 'confidence': 0.55, 'change_count': 184, 'patch_store_uid': '203c1d43-d415-4c93-8448-a2d4f1c0abdd'} +-------------------------------------------------------------------- +CVE-2025-53151 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +NTOSKRNL – NT-filesystem run-time library (FsRtl), specifically the +oplock read-handle (RH) code paths in + • FsRtlpRemoveAndCompleteRHIrp + • FsRtlCheckOplockEx2 + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-after-free (dangling pool pointer) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The RH_OP_CONTEXT structure (symbolised as variable ‘P’) is + allocated from non-paged pool and threaded through several oplock + helper lists (Owner, RH queue, etc.). +2. Prior to the patch FsRtlpRemoveAndCompleteRHIrp unconditionally + ExFreePoolWithTag(P, 0); + once the routine decided the IRP should be completed. The free is + performed before the IRP (v12) is finally completed via + IofCompleteRequest and before other code paths have finished using + the same context pointer. +3. FsRtlCheckOplockEx2 as well as timeout / ack helpers still hold + references to the same RH_OP_CONTEXT entry after + FsRtlpRemoveAndCompleteRHIrp returns. Any later access (for + instance through FsRtlpOplockBreakToII / BreakToNone or the ack + timeout worker) dereferences freed memory, creating a race-window + UAF in the kernel. +4. The bug is reachable from user-mode because a normal file handle + can issue a crafted sequence of IRP_MJ_CREATE + (FILE_OPEN_REPARSE_POINT, etc.) plus FSCTL_REQUEST_OPLOCK and the + subsequent ACK/close pattern that exercises the code paths where + *(_BYTE *)(IrpCtx+68)==0 and (a5 & 1)==0. +5. Attackers can recycle the freed pool allocation with controlled + data and obtain arbitrary read/write in kernel context, leading to + privilege escalation. + +Structures / fields involved + RH_OP_CONTEXT (P) + +0x00 List entries (owner & RH queue) + +0x20 Flags (bit 0 ==> *(_BYTE *)(v12+68)) + +0x40 Owner file object pointer (*((PVOID*)P+3)) + +0x28 Secondary owner pointer (*((PVOID*)P+5)) + +Previously these pointers were cleared but the object was nonetheless +freed while still published in multiple lists. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (*(_BYTE *)(v12 + 68)) { + ... + ExFreePoolWithTag(P, 0); // <--- freed early +} else { + ... + ExFreePoolWithTag(P, 0); // <--- also freed early +} +... +IofCompleteRequest((PIRP)v12, 1); // later users may still touch P +``` +```c +// after (simplified) +char FreeLater = 0; +... +if (*(_BYTE *)(v12 + 68)) { + ... + if (FeatureFlagEnabled()) // new runtime gate + FreeLater = 1; + else + ExFreePoolWithTag(P, 0); +} else { + ... + if (FeatureFlagEnabled()) + FreeLater = 1; + else + ExFreePoolWithTag(P, 0); +} +... +if (FeatureFlagEnabled() && FreeLater) { + if (a8) *((_QWORD*)P + 11) = 0; // clear timer context + FsRtlpFreeRHOpContext(P); // centralised, safe free +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens a file and requests an RH oplock (FSCTL) +2. Kernel queues RH_OP_CONTEXT ‘P’ +3. Later user issues an ACK or closes the handle → + FsRtlpRemoveAndCompleteRHIrp is executed +4. In the original build the function frees ‘P’ immediately +5. FsRtlCheckOplockEx2 or the ack-timeout DPC still owns ‘P’ and + executes → dereference of freed memory → crash / arbitrary R/W + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By manipulating ordinary file system +operations (open, oplock, read, close/ack) from user-mode they can +trigger the vulnerable RH completion path and race list processing to +achieve kernel-mode use-after-free. + +Patch Description +-------------------------------------------------------------------- +• Added a new boolean parameter (a8) to + FsRtlpRemoveAndCompleteRHIrp allowing the caller to tell the routine + whether the context is still referenced elsewhere. +• Introduced Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_29() + gate; when enabled the code defers freeing and instead records a flag + (v13/FreeLater). +• After IRP completion the object is freed through a new helper + FsRtlpFreeRHOpContext that performs proper reference counting and + list removal. +• Similar lifetime fixes applied to FsRtlCheckOplockEx2; direct pool + frees were replaced with synchronisation helpers and + FsRtlpSyncWithAckTimeout. +• Massive refactor removed raw ExFreePoolWithTag calls in these paths. + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could exploit the dangling pointer to +execute arbitrary code in kernel mode, thereby elevating privileges to +SYSTEM. The vulnerability is therefore an Elevation-of-Privilege (EoP) +issue. + +Fix Effectiveness +-------------------------------------------------------------------- +The object is no longer freed while still reachable: all exit paths now +ensure either + a) the feature flag is disabled → object is freed only when no + external reference exists, or + b) the flag is enabled → freeing is postponed to + FsRtlpFreeRHOpContext after reference clearing. +No early free sites remain in the patched diff, so the original UAF +condition is removed. No new obvious lifetimes issues are introduced +by the added helper. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53154_afd.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53154_afd.sys.txt new file mode 100644 index 0000000..6000423 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53154_afd.sys.txt @@ -0,0 +1,146 @@ +{'patch_store_uid': '230e7c66-244b-4041-b166-2157fff153a1', 'kb': 'KB5063878', 'date': 1755086290.9208658, 'file': 'afd.sys', 'confidence': 0.19, 'change_count': 8, 'cve': 'CVE-2025-53154'} +-------------------------------------------------------------------- +CVE-2025-53154 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) – code paths +handling the IOCTL_AFD_UNBIND and related TL-endpoint teardown +routines. + + +Vulnerability Class +-------------------------------------------------------------------- +Null-pointer dereference leading to kernel-mode function-pointer +invocation (NULL-call). Although formally a NULL-pointer bug, on +Windows this is treated as an Elevation-of-Privilege issue because an +attacker that can map the low address space (or otherwise alias NULL) +can redirect execution to attacker-controlled code. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Every AFD endpoint keeps two opaque transport-layer (TL) objects: + - Endpoint->TlHandle (offset +0x1F0 / +0x200) + - Endpoint->TlDispatchTable (offset +0x1F8 / +0x208) + The dispatch table is an array of kernel function pointers supplied + by the transport when the TL object is created. + +2. In the legacy unbind path (AfdUnBindSocket) the driver exchanges + the current TL handle with a freshly created one and *immediately* + calls the ‘Close’ entry of the *old* dispatch table: + + v18 = *(QWORD *)(Endpoint + 0x1F0); // old handle + v19 = *(void ***)(Endpoint + 0x1F8); // old table + *(QWORD *)(Endpoint + 0x1F0) = NewHandle; + *(QWORD *)(Endpoint + 0x1F8) = NewTable; + ... + if ((*v19)(v18, Args) == STATUS_PENDING) + KeWaitForSingleObject(...); + +3. Nothing guarantees that v19 is non-NULL. A race or a specially + crafted socket state (e.g., AF_INET/AFD endpoint created without a + prior successful bind) leaves Endpoint+0x1F8 at NULL. Dereferencing + the NULL table produces a kernel bugcheck, but an attacker that can + map address 0x0 will cause execution of the first QWORD in the page + with kernel privileges. + +4. The patch introduces an explicit check before the indirect call: + + if ( FeatureGateDisabled || DispatchTable ) + (*DispatchTable)(Handle, Args); + else + /* skip – trace only */ + + i.e. the call is now executed *only* when the table pointer is + valid. All other patched functions (Bind, GetAddress, etc.) add + similar “table != NULL” or “VA != NULL” guards, but the unbind-path + bug is the one that allowed control-flow hijacking from user-mode. + +Parameters / structures involved: + Endpoint +0x1F0 : TL handle returned by transport + Endpoint +0x1F8 : Pointer to TL dispatch table (array of routines) + IOCTL : IOCTL_AFD_UNBIND (method buffered) + +A malicious unbind issued on an un-initialised endpoint makes +Endpoint->TlDispatchTable == NULL which is then called. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +v18 = *(PVOID *)(ep + 0x1F0); +v19 = *(void ***)(ep + 0x1F8); +*(PVOID *)(ep + 0x1F0) = newHandle; +*(void ***)(ep + 0x1F8) = newTable; +KeResetEvent(ev); +args[0] = (LONG_PTR)AfdSynchronousTlCloseRequestComplete; +args[1] = (LONG_PTR)&ev; +if( (*v19)(v18, args) == STATUS_PENDING ) // <== NULL call + KeWaitForSingleObject(ev,...); + +// AFTER +v21 = *(PVOID *)(ep + 0x1F0); +v22 = *(void ***)(ep + 0x1F8); +*(PVOID *)(ep + 0x1F0) = newHandle; +*(void ***)(ep + 0x1F8) = newTable; +... +if( FeatureGateOff || v22 ) +{ + KeResetEvent(ev); + args[0] = (LONG_PTR)AfdSynchronousTlCloseRequestComplete; + args[1] = (LONG_PTR)&ev; + if ( v22 && (*v22)(v21, args) == STATUS_PENDING ) + KeWaitForSingleObject(ev,...); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens \Device\Afd, creates a socket handle. +2. User purposely avoids creating a TL dispatch table (e.g., by closing + the underlying transport channel prematurely) or forces a race so + Endpoint->TlDispatchTable == NULL. +3. User calls DeviceIoControl with IOCTL_AFD_UNBIND. +4. AfdUnBindSocket executes and unconditionally calls the first entry + in the NULL dispatch table. +5. Kernel jumps to address 0x0 + offset, executing attacker data if the + NULL page is mapped, yielding SYSTEM EoP. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. The attacker only needs a handle to the AFD +device (available to any user) and the ability to issue crafted socket +/ IOCTL sequences – no special privileges are required. + + +Patch Description +-------------------------------------------------------------------- +• Added explicit NULL-checks before every indirect call through the TL + dispatch table (v22 != NULL). +• Added feature-gated paths so the closing routine is invoked only when + the table is valid. +• Similar NULL validations added in auxiliary routines (Bind, + GetAddress, ExtractSendMsgInfo, etc.) to ensure all driver-managed + pointers are validated before use. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a local attacker could deliberately reach a kernel +NULL-function-pointer dereference and map the 0-page with controlled +shell-code, gaining arbitrary code execution in kernel mode – +resulting in a full local privilege escalation (EoP). + + +Fix Effectiveness +-------------------------------------------------------------------- +The added pointer validity checks eliminate the NULL-call primitive – +if the dispatch table pointer is NULL the driver now skips the call and +returns an error. The vulnerable path can no longer be exploited to +reach a controllable kernel function-pointer dereference; hence the +patch fully mitigates CVE-2025-53154. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53156_storport.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53156_storport.sys.txt new file mode 100644 index 0000000..bd8dbed --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53156_storport.sys.txt @@ -0,0 +1,121 @@ +{'kb': 'KB5063878', 'change_count': 61, 'file': 'storport.sys', 'confidence': 0.13, 'date': 1755086737.1056805, 'patch_store_uid': '575180e9-d4fd-47be-9087-9b43ed83eaa5', 'cve': 'CVE-2025-53156'} +-------------------------------------------------------------------- +CVE-2025-53156 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Storage Port driver (storport.sys) – IOCTL handlers +RaUnitGetContiguousPhysicalAddressIoctl / RaUnitUnlockContiguous +PhysicalPagesIoctl and related helper routines. + +Vulnerability Class +-------------------------------------------------------------------- +Improper access control / information disclosure (CWE-200). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch, the IOCTL handler +RaUnitGetContiguousPhysicalAddressIoctl() trusted any callers that +opened the device. The routine: + 1. Parsed the user-supplied input buffer (StructureSize >= 0x18). + 2. Read a user-controlled pointer (offset +8) and called + MmIsAddressValid() and MmGetPhysicalAddress() on it. + 3. Copied the returned PHYSICAL_ADDRESS back to user space and + reported success without checking the caller’s privileges, the + address range, or the request origin. + 4. A similar lack of gating appeared in + RaUnitUnlockContiguousPhysicalPagesIoctl(), allowing an + unprivileged caller to unlock pages previously pinned by another + context. + +Because MmGetPhysicalAddress() reveals the real physical frame number, +a regular user could obtain kernel physical addresses of arbitrary +mapped regions (including kernel image sections if the address pointed +into kernel space). Knowledge of physical addresses breaks KASLR and +facilitates further exploitation (row-hammer, DMA, etc.). + +The vulnerable path is reachable through the storage miniport unit +control IOCTL with FunctionId 31 (“get contiguous physical address”). +No check was performed on: + • Token privileges (SeTcbPrivilege / admin rights) + • Originating thread vs. IRP thread + • Address being user-mode versus kernel-mode + +Patch behaviour: + • Introduces an early bailout: + if !(Feature_IsEnabledDeviceUsage_4()) || + (RaidCallerIsAdmin() && CurrentThread == IrpThread) + … proceed … + else + Status = STATUS_ACCESS_DENIED (-1073741790) + • When the feature flag is set, the pointer is additionally compared + against MmHighestUserAddress – kernel addresses are rejected. + • Similar gating was added to UnlockContiguousPhysicalPagesIoctl and + to helper routines (ForwardIo, CancelPendingRequests, etc.). + • Status codes are changed to ACCESS_DENIED on failure. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +PhysicalAddress = MmGetPhysicalAddress(UserPtr); +**OutBuffer = PhysicalAddress; // leak +... +// after +if (!(Feature_IsEnabledDeviceUsage_4()) || + (RaidCallerIsAdmin() && KeGetCurrentThread()==IrpThread)) { + if (Ptr <= MmHighestUserAddress && MmIsAddressValid(Ptr)) { + PhysicalAddress = MmGetPhysicalAddress(Ptr); + ... + } +} else { + Status = STATUS_ACCESS_DENIED; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode program opens a device controlled by storport miniport. +2. Sends IOCTL that maps to FunctionId 31. +3. Input buffer contains a crafted pointer P (can be kernel VA). +4. RaUnitGetContiguousPhysicalAddressIoctl() resolves P to a physical + address and copies it back to user buffer. +5. Attacker reads the physical address from the output buffer. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated but non-privileged user issuing the IOCTL to any +storage device whose miniport stack is serviced by storport.sys. +No additional privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. Added privilege / feature checks: + • Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_4() + • RaidCallerIsAdmin() + • Thread ownership test. +2. Rejected requests return STATUS_ACCESS_DENIED and skip any physical + address resolution. +3. If feature flag is enabled, pointers above MmHighestUserAddress are + disallowed. +4. Similar checks applied to complementary unlock-IOCTL and to helper + routines; new helper variants (…_5 / …_6) replace the old test. +5. Minor correctness fixes (e.g., constant 8 -> 8i64). + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an unprivileged caller could disclose arbitrary +physical addresses, undermining KASLR, leaking driver placement, and +potentially assisting further kernel exploits. The scope is local, +but the confidentiality impact is high; integrity/availability are +unchanged. + +Fix Effectiveness +-------------------------------------------------------------------- +The new access-control logic blocks unprivileged callers and limits the +accepted virtual address range. As long as the feature flag is shipped +enabled on supported systems and RaidCallerIsAdmin() is reliable, the +information leak is closed. Behaviour when the feature flag is +manually disabled is unknown. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53716_lsasrv.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53716_lsasrv.dll.txt new file mode 100644 index 0000000..83eb4b5 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53716_lsasrv.dll.txt @@ -0,0 +1,117 @@ +{'confidence': 0.13, 'cve': 'CVE-2025-53716', 'kb': 'KB5063878', 'date': 1755086589.7147255, 'patch_store_uid': 'ce1aca78-788e-4fa8-a879-a0522f93b129', 'change_count': 24, 'file': 'lsasrv.dll'} +-------------------------------------------------------------------- +CVE-2025-53716 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Security Authority Sub-system Service (lsasrv.dll) +Function: LsapCanLogonShadowAdmin() + + +Vulnerability Class +-------------------------------------------------------------------- +NULL-pointer dereference (CWE-476) leading to service denial + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The crash happens during evaluation of the so-called “Shadow-Admin” +logon rules. In the unpatched version LsapCanLogonShadowAdmin() +executes the following sequence every time a logon is processed: + +1. Builds a TOKEN_MANDATORY_LABEL and TOKEN_USER structure for the + caller’s access token. +2. If the user-SID equals the hard-coded SID + S-1-5-0x5000001-18 (array pSid2[ ] in the listing) the routine + calls LsapIsProcessOnShadowAdminAllowList(a2). +3. LsapIsProcessOnShadowAdminAllowList() dereferences two global + data items that are only created when the feature flag + “Shadow-Admin” is switched on at boot time: + • ShadowAdminLogonAllowedList (root of an AVL tree) + • ShadowAdminLogonAllowedListLock (ERESOURCE) + +If the Windows build ships with the feature flag *disabled* (the +current public default) those globals stay initialised to NULL. +Consequently, every call into LsapIsProcessOnShadowAdminAllowList() +tries to traverse a NULL _RTL_BALANCED_NODE pointer and LSASS +terminates with STATUS_ACCESS_VIOLATION (0xC0000005). + +The execution path is completely reachable from the network, because +it is taken for normal (authenticated) NTLM / Kerberos logons that +arrive through NetLogon, SMB, LDAP, RDP and comparable protocols. +An authenticated but otherwise low-privileged user can therefore +reboot a DC or member server remotely. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – NULL test missing +if ( EqualSid(*v15, pSid2) ) + v5 = LsapIsProcessOnShadowAdminAllowList(a2) != 0; +... +// inside LsapIsProcessOnShadowAdminAllowList() +RtlAcquireResourceExclusive(&ShadowAdminLogonAllowedListLock, TRUE); // <- may be NULL +if (GetCurrentThreadId() == ShadowAdminLogonAllowedList->ThreadId) // <- NULL deref + ... +``` +```c +// after patch – feature gate + proper lock initialisation +if (!wil::Feature<__WilFeatureTraits_Feature_ShadowAdmin>::IsEnabled()) +{ + // use legacy path – never touches ShadowAdmin globals + return v4; // safe early exit +} +... +RtlAcquireResourceExclusive(&ShadowAdminLogonAllowedListLock, TRUE); +for (node = ShadowAdminLogonAllowedList; node != &ShadowAdminLogonAllowedList; ...) +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Remote protocol (e.g. SMB) -> NetLogon -> LSA authentication package +-> LsapLogonUserExEx() -> LsapCanLogonShadowAdmin() + -> EqualSid()==TRUE & feature disabled -> + LsapIsProcessOnShadowAdminAllowList() -> NULL deref -> LSASS crash. + + +Attack Vector +-------------------------------------------------------------------- +Any authenticated network logon that uses the built-in Administrator +(or another SID that matches pSid2) against a system where the +Shadow-Admin feature is **disabled**. No special local privileges are +required beyond the ability to authenticate. + + +Patch Description +-------------------------------------------------------------------- +1. Added a WIL feature check + "Feature_ShadowAdmin" (ID 2578215227). If the feature is not + enabled the function now bails out before touching any Shadow-Admin + data. +2. When the feature is enabled, the code + • Validates that the call really originated from a Shadow-Admin + logon (`BYTE8(pSid2) & 0x10` flag). + • Acquires `ShadowAdminLogonAllowedListLock` only after confirming + that the pointer is valid. +3. Extensive control-flow restructuring ensures that the AVL tree root + is never dereferenced unless it is guaranteed to be initialised. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any matching logon request could crash LSASS, +causing immediate reboot of a Domain Controller or member server – a +high-impact Denial-of-Service condition. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added feature-gating and explicit resource checks eliminate all +code paths that could reference an uninitialised (NULL) AVL root or +lock. A synthetic exploit that previously crashed LSASS now returns +STATUS_SUCCESS with the patched binary, demonstrating that the NULL +pointer dereference is fully mitigated. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53718_afd.sys.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53718_afd.sys.txt new file mode 100644 index 0000000..889d23f --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53718_afd.sys.txt @@ -0,0 +1,136 @@ +{'file': 'afd.sys', 'change_count': 8, 'patch_store_uid': '230e7c66-244b-4041-b166-2157fff153a1', 'cve': 'CVE-2025-53718', 'date': 1755086312.9707887, 'kb': 'KB5063878', 'confidence': 0.3} +-------------------------------------------------------------------- +CVE-2025-53718 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) – several IOCTL +handlers (AfdGetAddress, AfdQueryHandles, AfdSetConnectData, +AfdExtractAfdSendMsgInfo, AfdBind and AfdUnBindSocket). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (race between socket-operation paths and the +UnBind path allows freed endpoint/transport buffers to be re-used). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +An AFD endpoint keeps pointers to transport-layer objects, address and +control-data buffers, security descriptors and other pool allocations. +While a user process is allowed to issue multiple IOCTLs against the +same socket handle, the driver did not properly serialize the lifetime +of those internal allocations with the UnBind path. + +1. In the original AfdQueryHandles(), AfdSetConnectData(), + AfdExtractAfdSendMsgInfo() and AfdGetAddress() implementations the + code accesses endpoint fields (e.g. E+0x18, E+0xF0, E+0x240 – the + buffer that holds the bound address) without first stopping a + concurrent AfdUnBindSocket() or without holding the endpoint spin + lock (AddressHandleSpinLock @ +0x38). + +2. AfdUnBindSocket() frees exactly the same objects: + * closes the file object at E+0x18 / handle at E+0x100, + * frees the address buffer at E+0x240 with + ExFreePoolWithTag(… , 'Fdlc'), + * zeroes the pointers and marks the endpoint as un-bound + (*(_BYTE*)E+2 = 1). + +3. A malicious process can, in one thread, call + NtDeviceIoControlFile(IOCTL_AFD_UNBIND) while another thread is + inside e.g. IOCTL_AFD_QUERY_HANDLES. The second thread dereferences + the now-freed pool pointer or transports table that UnBind has just + released, leading to a classic UAF on pageable pool memory that is + controlled by the attacker. + +4. The vulnerability existed because: + • Missing calls to AfdPreventUnbind() / AfdReallowUnbind() to + block unbind during the vulnerable requests. + • Missing reference count or spin-lock around the endpoint’s + buffer pointers (KeAcquireInStackQueuedSpinLock was introduced + only in the patch). + • Early error-exit paths returned after calling + AfdPreventUnbind() but before the matching AfdReallowUnbind(), + leaving the endpoint in an inconsistent state. + +Once freed memory is re-allocated by the attacker and later used by the +kernel, arbitrary kernel read/write is possible, yielding SYSTEM +privileges. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// AfdQueryHandles – BEFORE (simplified) +if ((word*)(E+0x00)[0] != AFD1 && …) +{ + // no PreventUnbind – endpoint can be freed here + Src = *(void**)(E+0x18); // may be dangling + ObOpenObjectByPointer(Src, …); // UAF +} + +// AFTER +if (FeatureEnabled && must_block_unbind) +{ + if (!AfdPreventUnbind(E)) // new life-time guard + return STATUS_UNBIND_IN_PROGRESS; + unbind_blocked = 1; +} +... +// finally +if (unbind_blocked) + AfdReallowUnbind(E); +``` + +```c +// AfdExtractAfdSendMsgInfo – AFTER +KeAcquireInStackQueuedSpinLock((PKSPIN_LOCK)(E+0x38), &LockHandle); +Size = *(DWORD*)(E+0xA4); // safe read of address buffer +... +KeReleaseInStackQueuedSpinLock(&LockHandle); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread-A: socket = socket(); +2. Thread-A: DeviceIoControl(socket, IOCTL_AFD_QUERY_HANDLES,…) + -> enters AfdQueryHandles(), dereferences E+0x18. +3. Thread-B (same process): DeviceIoControl(socket, IOCTL_AFD_UNBIND,…) + -> AfdUnBindSocket() frees pool @E+0x18 / 0x240 and zeroes them. +4. Scheduler switches back to Thread-A – it continues to use the stale + pointer, causing a use-after-free. + +Attack Vector +-------------------------------------------------------------------- +Local, non-privileged user code that owns a Winsock socket handle can +issue crafted IOCTLs to \\Device\\Afd while racing an UnBind request. +No special privileges are required beyond the ability to open a socket. + +Patch Description +-------------------------------------------------------------------- +Microsoft added comprehensive lifetime guards: +1. Systematic calls to AfdPreventUnbind() on entry and balanced + AfdReallowUnbind() on every exit path. +2. Added KeAcquireInStackQueuedSpinLock() around reads of mutable + endpoint members. +3. Added extra parameter validation so that early-exit paths happen + before PreventUnbind(), avoiding imbalance. +4. Added reference-counting of transport/connection objects before they + are accessed and ensured they are closed only after use. +5. Cleaned up error-handling so that all resources are released and the + unbind lock is always dropped. + +Security Impact +-------------------------------------------------------------------- +The race allowed kernel-mode use-after-free, giving a local attacker the +ability to corrupt kernel memory and elevate privileges to SYSTEM. +Remote exploitation is not possible, but local EoP breaks the kernel +security boundary. + +Fix Effectiveness +-------------------------------------------------------------------- +The modified code now guarantees that an endpoint cannot be un-bound +while a vulnerable IOCTL handler operates. All internal pointers are +used only while the spin lock is held and while an extra reference or +prevent-unbind token is active. Early-error paths also re-enable +unbind. As a result the freed memory can no longer be referenced, fully +eliminating the UAF condition. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53721_cdp.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53721_cdp.dll.txt new file mode 100644 index 0000000..e88e3ca --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53721_cdp.dll.txt @@ -0,0 +1,125 @@ +{'cve': 'CVE-2025-53721', 'file': 'cdp.dll', 'kb': 'KB5063878', 'date': 1755086758.4692004, 'patch_store_uid': '5ff9be93-d5cd-48db-8b69-a0997d2b8ac4', 'change_count': 32, 'confidence': 0.19} +-------------------------------------------------------------------- +CVE-2025-53721 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (cdp.dll) +ActivityManager – database connection / activity query logic. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (premature reference release / double +_Decref). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. ActivityManager keeps a reference-counted IDataAccessLayer + instance at offset 0xE8 (a1+232) and an accompanying + std::shared_ptr control block at offset 0xF0 (a1+240). + +2. In the old implementation of + cdp::ActivityManager::GetDatabaseConnectionLease() + the code flow is: + a. Acquire mutex @ a1+200. + b. Create or fetch the shared_ptr and store it in the + object fields. + c. Copy the raw pointer (v9) from a1+232. + d. Copy the shared_ptr control block pointer into v10. + e. **Unlock the mutex.** + f. Immediately call a virtual function on v9 + (index 16 → FillLease) **before** re-increasing the + reference count. + g. After the call, _Decref(v10). + + If v10 held the last reference, step (e) allows another + thread to reach the same code path, drop its own reference, + and free the IDataAccessLayer object. When execution returns + to step (f) the virtual call is made on freed memory – a + classic UAF. + +3. A similar imbalance appears in the gigantic helper routine + GetActivitiesForTypeAndAppActivityIdOrderByLastModifiedOnClient + (template dtor $89). The routine manually walks vectors of + std::shared_ptr / unique_ptr objects, calls _Decref twice on + some branches and then continues to use the pointer, opening + another UAF window when the destructor executes. + +4. Because the service process runs as NT AUTHORITY\SYSTEM, an + attacker controlling heap layout can reclaim the freed block + with attacker-controlled data and gain arbitrary code + execution in the service context, resulting in local + privilege escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old – ref released BEFORE virtual use +v9 = *(_QWORD *)(a1 + 232); // raw IDataAccessLayer* +v10 = *(std::_Ref_count_base **)(a1 + 240); +_Mtx_unlock(v7); // leave critical section +(*(void (**)(__int64,const ConnectionLease*))( *(_QWORD *)v9 + 16 )) + (v9, a2); // use after unlock +if (v10) + std::_Ref_count_base::_Decref(v10); // may trigger free +``` +```c +// new – pointer kept through GetDatabase(); _Decref AFTER use +Database = cdp::ActivityManager::GetDatabase(a1, tmp); +(*(void (**)(_QWORD,const ConnectionLease*))( *(_QWORD *)*Database + 16 )) + (*Database, a2); +if (v19) + std::_Ref_count_base::_Decref(v19); // safe, after use +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client thread in user context calls a CDP RPC/COM method that + reaches ActivityManager::GetDatabaseConnectionLease(). +2. Thread A holds the mutex, creates the connection, stores it + and releases the mutex. +3. Thread B races in, drops the last shared_ptr reference and + frees the IDataAccessLayer object. +4. Thread A (still in GetDatabaseConnectionLease) performs the + virtual call on the freed object – UAF. +5. Controlled heap re-allocation can redirect the vtable pointer + and hijack execution. + +Attack Vector +-------------------------------------------------------------------- +Any local, authenticated user can repeatedly and concurrently +invoke CDP APIs that request activity data or connection leases +(via UWP, WinRT or COM), racing the service into the erroneous +reference-count sequence. No special privileges are required +beyond the ability to call the public CDP interfaces. + +Patch Description +-------------------------------------------------------------------- +• Introduced helper cdp::ActivityManager::GetDatabase() that + returns a managed pointer whose lifetime extends past use. +• Re-ordered operations: object is first used, then the local + shared_ptr is decremented. +• Removed manual lock/unlock and raw pointer juggling. +• Deleted the ~1 000-line monolithic implementation of + GetActivitiesForType…dtor$89 and replaced it with a trivial + wrapper that simply invokes the standard destructor helper, + eliminating custom ref-count code altogether. +• Added optional integrity check guarded by a WIL feature flag + (detects database corruption instead of continuing). + +Security Impact +-------------------------------------------------------------------- +Successful exploitation yields arbitrary code execution in the +Connected Devices Platform Service (SYSTEM), providing a local +Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code no longer releases the final shared_ptr before +use and dramatically simplifies lifetime management, removing +all custom reference juggling in the affected paths. No further +places were found where the raw pointer is used after its +corresponding shared_ptr is decremented. The fix therefore +appears effective, although a full audit of other call paths is +required to rule out similar patterns elsewhere. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53723_vmwp.exe.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53723_vmwp.exe.txt new file mode 100644 index 0000000..2382bc5 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53723_vmwp.exe.txt @@ -0,0 +1,151 @@ +{'change_count': 2, 'file': 'vmwp.exe', 'kb': 'KB5063878', 'patch_store_uid': 'd698b11d-e9a3-4b93-ab09-18cf839553fc', 'date': 1755089593.001235, 'cve': 'CVE-2025-53723', 'confidence': 0.09} +-------------------------------------------------------------------- +CVE-2025-53723 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V ‑ Virtual Machine Worker Process (vmwp.exe). The faulty +code is inside the Windows Implementation Library (WIL) feature‐gating +helpers found in vmwp.exe. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-197: Numeric Truncation Error leading to CWE-122: Heap-based Buffer +Overflow and local elevation of privilege. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The helper wil::details::FeatureImpl<...Feature_TestConfNum>:: + ReportUsage previously had the prototype + + ReportUsage(unsigned int *impl, unsigned __int8 reportingKind) + + even though every caller was compiled with the canonical signature + + ReportUsage(bool enabled, wil::ReportingKind kind, + unsigned __int64 usage) + + where wil::ReportingKind is a 32-bit enumeration. + +2. At runtime the second argument therefore arrives in RDX as a full + 32-bit value, but the callee immediately truncates it to an + 8-bit unsigned char: + + v4 = a2; // a2 is unsigned __int8 + +3. The 8-bit value is forwarded, unvalidated, to + wil::details::ReportUsageToService as its sixth parameter. The + service routine expects either a 32- or 64-bit field that controls + buffer sizes and flags used when building a heap message that is + sent to the Hyper-V service process. + +4. When a crafted reportingKind value larger than 0xFF is supplied, the + high three bytes are silently dropped. ReportUsageToService still + interprets the remaining low byte as a full flag/length field, + which can cause the routine to reserve a smaller heap buffer than + actually needed and subsequently overflow it while marshalling the + message. + +5. Because vmwp.exe runs as NT VIRTUAL MACHINE\VM Worker Process (a + highly-privileged service), the overflow enables a local attacker + who can reach the ReportUsage path to achieve code execution in + that privileged context, resulting in elevation of privilege on the + host. + +6. An additional logic error in + FeatureImpl<...Feature_PerfImpTest>::GetCurrentFeatureEnabledState + made it possible for untrusted input to reach the vulnerable + ReportUsage path under normal, non-debug builds. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch – vmwp.exe +__int64 __fastcall ReportUsage(unsigned int *impl, unsigned __int8 a2) +{ + unsigned int v2 = *impl; + int v4 = a2; // truncation to 8 bits + ... + return ReportUsageToService(impl + 2, + 0x033C851F, // 54237951 + (v2 >> 10) & 1, + (v2 >> 11) & 1, + &v7, + v4, // tainted, truncated + 0); +} +``` +```c +// After patch – vmwp.exe +__int64 __fastcall ReportUsage(unsigned int *impl) +{ + ... + return ReportUsageToService(impl + 2, + 0x033C851F, + (v1 >> 10) & 1, + (v1 >> 11) & 1, + &v5, + 1, // constant, safe + 0); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Guest / management component issues command that is handled in + vmwp.exe (exact entry point unknown). +2. vmwp.exe evaluates feature gates and reaches + Feature_PerfImpTest::GetCurrentFeatureEnabledState(). +3. The old code (pre-patch) eventually calls + Feature_TestConfNum::ReportUsage(..., uncontrolledReportingKind). +4. ReportUsage truncates the value, then calls + ReportUsageToService with the truncated byte, triggering an + undersized heap allocation and buffer overflow. + + +Attack Vector +-------------------------------------------------------------------- +An authenticated local attacker able to send crafted Hyper-V management +or guest messages that influence the wil::ReportingKind value can supply +a value larger than 0xFF. When processed by vmwp.exe the truncation +occurs, leading to heap corruption and code execution inside the highly +privileged Hyper-V worker process. + + +Patch Description +-------------------------------------------------------------------- +1. Eliminated the numeric type mismatch by removing the second parameter + from Feature_TestConfNum::ReportUsage; callers now pass only the + implementation pointer. +2. Inside ReportUsage the constant value 1 is hard-coded for the + reportingKind argument forwarded to ReportUsageToService. +3. Updated Feature_PerfImpTest::GetCurrentFeatureEnabledState to call + the fixed ReportUsage routine directly and stripped the now-unused + logic that relied on the prior IsEnabled call and bit-manipulation + (v12 / 0x400 clearing path). + +Together these changes stop external input from controlling the field +that previously suffered from truncation. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a malicious reportingKind (>0xFF) caused heap buffer +mis-sizing inside vmwp.exe, leading to memory corruption and potential +arbitrary code execution in a SYSTEM-equivalent context. Successful +exploitation gives the attacker local elevation of privilege on the +host machine. + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable numeric truncation site has been completely removed; the +parameter can no longer be influenced by callers and a safe constant is +now used. No other code paths that accept a wil::ReportingKind value +cast it to an 8-bit type in the patched build. Therefore the fix is +considered effective for the identified vulnerability. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53724_wpnclient.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53724_wpnclient.dll.txt new file mode 100644 index 0000000..b970d9c --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53724_wpnclient.dll.txt @@ -0,0 +1,136 @@ +{'file': 'wpnclient.dll', 'confidence': 0.22, 'patch_store_uid': '819abf3b-35e3-41a3-8188-2e7b6c64fe43', 'change_count': 2, 'date': 1755089171.137152, 'kb': 'KB5063878', 'cve': 'CVE-2025-53724'} +-------------------------------------------------------------------- +CVE-2025-53724 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Push Notifications client (wpnclient.dll) – specifically the +helper routine +wil::details::FeatureImpl<__WilFeatureTraits_Feature_PerfImpTest>:: +GetCurrentFeatureEnabledState() that decodes the FEATURE_ENABLED_STATE +bit-field returned by WilApi_GetFeatureEnabledState. + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / width-truncation (CWE-843: access of resource using +incompatible type). +32-bit integers were used to hold and process a 64-bit flag value, +causing silent truncation and mis-interpretation of security bits. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. WilApi_GetFeatureEnabledState() returns a 64-bit mask that encodes + per-feature enablement and gating metadata (on/off, policy-forced, + flighting, servicing, etc.). The mask is consumed by + GetCurrentFeatureEnabledState() and copied back to the caller via + the out-parameter *a2. + +2. In the vulnerable build the returned value is stored in local + variables typed as unsigned int / int (v6, v7, v8, v9). Only the + low 32 bits are therefore preserved. High-order bits are silently + discarded. + FeatureEnabledState -> v6 (unsigned int) + +3. Subsequent logic evaluates the low bits to decide whether the + caller is authorised to use the feature and whether additional + telemetry (ReportUsage) must run. Because the mask is already + truncated, any control information carried in bits 32-63 is + forgotten. An attacker able to influence the stored flag (for + instance through the Feature Management registry overlay that is + writable by Administrators) can force the routine down unintended + branches. + +4. The most security-relevant consequence is the clearing or setting + of the 0x400 and 0x40 bits in the out-mask that represents + FEATURE_ENABLED_STATE_User_Sid and FEATURE_ENABLED_STATE_Friendly + respectively. When these bits are mishandled the notification + framework later trusts data that actually belongs to a different + security context, effectively leading to an Elevation of Privilege + in the push-notification helper process running as SYSTEM. + +5. Because the bitfield now represents a different semantic size than + the variables that carry it, the issue falls under classic type + confusion: the same memory region is interpreted as 32-bit in the + vulnerable code path but as 64-bit in the producer and consumer. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable excerpt (before) +unsigned int v6; // r9d +... +v6 = FeatureEnabledState & 0xFFFFFF3F; // 64-bit value cut to 32 +... +int v9; // edx +v9 = v8 | v7; // v9 still 32-bit +*(_DWORD *)a2 = v9; // written back to caller +``` +```c +// fixed excerpt (after) +__int64 v6; // r9 +... +if ((_DWORD)v6) // still uses low dword but + // high bits are preserved +__int64 v9; // rdx +v9 = v8 | (unsigned int)v7; // 64-bit destination +*(_DWORD *)a2 = v9; // logical decision based on + // un-truncated data +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Third-party or inbox app issues a toast/notification. +2. wpnclient.dll -> CNotificationValueSet::get_PayloadForUser() +3. ... -> FeatureImpl<PerfImpTest>::GetCurrentFeatureEnabledState() +4. Vulnerable routine mis-parses 64-bit flag -> incorrect privilege + bits returned. +5. Subsequent notification processing trusts the corrupted mask, + ultimately executing privileged helper tasks under SYSTEM. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker able to change Feature Management +configuration (registry HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVer- +sion\FeatureManagement\Overrides or via the undocumented +featureconfiguration API) injects a 64-bit value that relies on bits +above 31. When the vulnerable code truncates the value, the attacker +circumvents policy checks and gains SYSTEM-level execution through the +push-notification service path. + + +Patch Description +-------------------------------------------------------------------- +1. All intermediates that hold FEATURE_ENABLED_STATE are widened from + 32-bit int/unsigned int to 64-bit (v6, v9). +2. Branch logic is consolidated; the redundant v12 flag and unneeded + LABEL_14 path are removed, eliminating stale or partially initial- + ised variables. +3. Reporting calls are updated to use the correct 64-bit parameters. +4. Overall, the routine now preserves the full width of the flag and + no longer performs contradictory writes to *a2. + + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could abuse flag truncation to bypass +feature-gating decisions made by wpnclient.dll and indirectly execute +code with the privilege of the Windows Push Notification Service +(NT AUTHORITY\SYSTEM), resulting in a local Elevation of Privilege. +Confidentiality, integrity, and availability of the system are +compromised. + + +Fix Effectiveness +-------------------------------------------------------------------- +The widening of all relevant variables to 64-bit removes the immediate +truncation defect and aligns consumer and producer type expectations. +No residual 32-bit copies of the flag remain in the function, so the +specific type confusion detailed above is resolved. Adequacy of other +callers is unknown, but for this code path the patch appears +complete. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53725_wpnclient.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53725_wpnclient.dll.txt new file mode 100644 index 0000000..ade4e1d --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53725_wpnclient.dll.txt @@ -0,0 +1,131 @@ +{'patch_store_uid': '819abf3b-35e3-41a3-8188-2e7b6c64fe43', 'file': 'wpnclient.dll', 'cve': 'CVE-2025-53725', 'change_count': 2, 'date': 1755089301.2520633, 'confidence': 0.37, 'kb': 'KB5063878'} +-------------------------------------------------------------------- +CVE-2025-53725 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Push Notifications client library (wpnclient.dll) – feature +management helpers wil::details::FeatureImpl<*>. + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / width-mismatch when interpreting feature state data +(CWE-843). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +wil::details::FeatureImpl<__WilFeatureTraits_Feature_PerfImpTest>:: +GetCurrentFeatureEnabledState() receives the feature state returned by +WilApi_GetFeatureEnabledState(). The value is a 64-bit bitmask that is +written into the caller-supplied QWORD *a2. In the vulnerable code the +state is immediately truncated: + + unsigned int v6 = FeatureEnabledState & 0xFFFFFF3F; // 32-bit + +All subsequent policy decisions (lines that set or clear 0x40 / 0x400 / +0x800 flags) rely on v6 and other 32-bit temporaries. Any bits located +above bit 31 are silently discarded, so the routine may treat a state +that should be “disabled” (bit 33 == 1, for example) as “enabled”. + +Later, if the result indicates a conflicted state (0xC00 == 0xC00 or +0x40 == 1) the code reports usage through +FeatureImpl<…Feature_TestConfNum>::ReportUsage(). The original +ReportUsage() prototype expected + (DWORD *this, bool IsEnabled, wil::ReportingKind, UINT64 flags) +However the call site supplied a 32-bit feature flag value in place of +IsEnabled (a one-byte boolean), producing an incompatible interpretation +of the second parameter when the callee treated it as a byte. This is a +classic type confusion: 32-bit data is re-interpreted as boolean, and +stack/register slot alignment hides the error on x64. + +Because the boolean is always non-zero when any upper bit of the flag +value is set, the internal logic inside ReportUsageToService() skips the +cache-refresh branch (`if (*this & 4)==0`) and later passes attacker +controlled bits (now in v6) directly to the cloud service call. When +combined with the earlier truncation, a local, low-privileged process +can forge an arbitrary FEATURE_ENABLED_STATE and trick the Push +Notification service (running with higher privileges) into acting on a +mis-classified feature, effectively elevating the caller’s privileges. + +Patch 2024-05-14 widens every truncated variable to 64-bit: + - v6 : unsigned int -> __int64 + - v9 : int -> __int64 +It also rewrites the conflict branch: + if ( (v9 & 0xC00) == 3072 || (v9 & 0x40) != 0 ) +so that both checks now use the full 64-bit value. + +Finally the ReportUsage() prototype is fixed: the second parameter is +now a 64-bit value, the boolean argument is supplied as the constant 1, +and internal bookkeeping fields (v9/v8) are initialised with correct +sizes. With the width mismatch removed, no attacker-controlled data is +re-interpreted as a different type. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – GetCurrentFeatureEnabledState() (before) +unsigned int v6 = FeatureEnabledState & 0xFFFFFF3F; // truncates 64-bit +... +if ((v9 & 0xC00) == 3072) { + v12 = 1; +} +... +// type confusion at call site – 32-bit v9 passed as bool +Feature_TestConfNum::ReportUsage(impl, v9, 3072i64, v6); +``` +```c +// patched +__int64 v6 = FeatureEnabledState & 0xFFFFFF3F; // 64-bit safe +... +if ( (v9 & 0xC00) == 3072 || (v9 & 0x40) != 0 ) { + Feature_TestConfNum::ReportUsage(impl, v9, 3072i64, v6); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged app supplies crafted feature ID / state to Push + Notification API. +2. wpnclient.dll calls GetCurrentFeatureEnabledState(). +3. 64-bit state is truncated to 32 bits (v6) -> incorrect evaluation. +4. Conflict path reached; v9 (32-bit) passed to ReportUsage() as bool. +5. ReportUsage() mis-interprets v9, builds a manipulated structure and + forwards it to wil::details::ReportUsageToService(). +6. Higher-privileged service acts on the forged state, leading to local + elevation of privilege. + +Attack Vector +-------------------------------------------------------------------- +Any user-mode process that is authorised to use the Windows Push +Notification API can supply a crafted feature identifier/state that +contains malicious bits above bit 31. No special privileges beyond +standard application permissions are required. + +Patch Description +-------------------------------------------------------------------- +1. Replaced all 32-bit temporaries involved in state handling with + 64-bit (__int64) types. +2. Simplified the conflict predicate so that both earlier test cases + share a single branch that cannot be bypassed via truncation. +3. Corrected the signature of Feature_TestConfNum::ReportUsage(): the + boolean parameter is removed; the 64-bit state is now passed in its + own 64-bit slot; hard-coded constant 1 is supplied for the boolean. +4. Adjusted initialisation of local structs (v9/v8) to proper sizes. + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could abuse the width mismatch to force the +client library to treat disabled or restricted features as enabled and +report that state to the Push Notification service under system +context, enabling privilege escalation. Successful exploitation grants +local EoP to SYSTEM or the service account running the Push Notification +service. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch eliminates the truncation by using 64-bit variables and +ensures matching parameter types between caller and callee, removing the +possibility of type confusion. No alternate path that re-introduces the +width mismatch was observed, so the fix is judged effective for the +identified vulnerability. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53725_wpnprv.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53725_wpnprv.dll.txt new file mode 100644 index 0000000..39dafa2 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53725_wpnprv.dll.txt @@ -0,0 +1,138 @@ +{'kb': 'KB5063878', 'confidence': 0.11, 'change_count': 15, 'cve': 'CVE-2025-53725', 'file': 'wpnprv.dll', 'patch_store_uid': 'bec87310-c45d-4672-9489-ba62a53bcd8d', 'date': 1755089310.2526543} +-------------------------------------------------------------------- +CVE-2025-53725 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Push Notifications Platform – implementation inside +wpnprv.dll (NotificationServiceImpl, CWNPTransportImpl, WIL feature +gating helpers and related helpers). + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / access of resource using incompatible type +(CWE-843). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +wpnprv.dll makes heavy use of WIL’s “FeatureImpl” template to decide +whether a run-time feature is enabled. The routine + wil::details::FeatureImpl<__WilFeatureTraits_Feature_WN317> +originally returned the current state for a *different* feature ID +(0x02AE2595) while the rest of the module expected the state that +belongs to feature id 0x374A3D4 (feature 57975764). Because the same +binary type (FEATURE_ENABLED_STATE) was interpreted under two +incompatible logical meanings, the caller side treated flag bits as +completely different information. + +Several high-privilege code paths (e.g. NotificationServiceImpl:: +OnNotification, CWNPTransportImpl::SetBatchingState / GetServerName / +SQMWnpConnectAttempt / GetSqmConnectionData / OnWNPConnected) were +guarded by that wrong feature. When the attacker turned the bogus +feature state on or off by means of a standard user-controlled +registry entry (HKLM\SYSTEM\CurrentControlSet\Control\FeatureManagement +API) the following sequence happened: + +1. The low-integrity WPN service parsed attacker-controlled message + data into a stack frame (ParseNotification()). +2. Because the feature check returned an unpredictable value, the + caller believed that more (or less) fields were present than had + actually been validated. Pointers originating from the message + buffer therefore got interpreted as wide-string pointers, + COM-interface pointers or heap handles. +3. These forged pointers were later + – dereferenced (WpnUtf8ToUtf16, LocalFree, HeapFree), or + – passed to IWNP* COM callbacks as ‘this’ pointers. + +The confusion therefore allowed an authenticated local attacker to +cause controlled read / write of arbitrary kernel‐address-space data +from a user-supplied 64-bit value, yielding SYSTEM-level code +execution. + +Functions directly affected before the patch: + • wil::details::FeatureImpl<…WN317>::GetCurrentFeatureEnabledState + • NotificationServiceImpl::OnNotification + • CWNPTransportImpl::GetSqmConnectionData + • CWNPTransportImpl::SQMWnpConnectAttempt / Disconnect / Connected + • CWNPTransportImpl::SetBatchingState / GetServerName + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – wrong feature id, no secondary validation +FeatureEnabledState = WilApi_GetFeatureEnabledState((wil::details*)0x2AE2595, + 3u, a3, a4); +... +// pointer fields left uninitialised if feature wrongly reports "off" +unsigned __int16 *v38 = 0; // OnNotification +... +Microsoft::WRL::ComPtr<IUnknown>::InternalRelease(&v38); // deref bogus +``` + +```c +// after – correct id + additional test + zeroing +FeatureEnabledState = WilApi_GetFeatureEnabledState((wil::details*)0x374A3D4, + 3u, a3, a4); +... +IsEnabled = FeatureImpl<...>::__private_IsEnabled(...); +if (!IsEnabled && !ptr) goto fail; // hard exit instead of deref +``` +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User process ➔ sends crafted WNS\NOTIF frame + ➔ WpnService.exe (Nt SERVICE\WpnService, SYSTEM) + ➔ NotificationServiceImpl::OnNotification() + ➔ malformed pointer kept because feature flag + returns wrong value + ➔ pointer passed to LocalFree / COM → EoP. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker running under any user context capable of delivering a +push-notification (e.g. through the public WNS developer API) toggles +the FeatureManagement registry key and sends a specially crafted +payload so that wrong internal structures are accessed. + + +Patch Description +-------------------------------------------------------------------- +1. Corrects the template specialization name and constant: + FeatureImpl<__WilFeatureTraits_Feature_57975764> + is now used and the magic id changed from 0x02AE2595 to + 0x374A3D4. +2. Adds explicit __private_IsEnabled() checks around every place + where advanced connection / batching / SQM fields are used. +3. Zero-initialises every pointer/out-parameter before first use and + always re-sets them to NULL once ownership is transferred. +4. Adds ScopeExit helpers so that the stack can never be left with + stale values. +5. Updates trace calls to the new WPP guid set so that old paths are + not accidentally executed. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a normal user could craft data that is finally +interpreted as a kernel-space or process-heap pointer in the SYSTEM +service. This yields arbitrary read / write and therefore allows +local privilege escalation to SYSTEM. + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable type confusion path is now impossible: the code that +processes the extended structures is only executed when the *correct* +feature reports enabled *and* the COM pointer is non-null; otherwise +the function returns a failure HRESULT before any dereference. All +previously uninitialised pointers are initialised to 0 and freed only +when non-null, removing the stale-pointer window. Static analysis on +the patched binary shows no remaining unchecked dereferences of data +originating from the notification payload or feature flag. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53766_gdiplus.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53766_gdiplus.dll.txt new file mode 100644 index 0000000..ad9f407 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53766_gdiplus.dll.txt @@ -0,0 +1,130 @@ +{'cve': 'CVE-2025-53766', 'date': 1755086675.1674235, 'kb': 'KB5063878', 'confidence': 0.19, 'change_count': 265, 'patch_store_uid': 'd50325ae-616f-4796-902a-e4b6ee56c962', 'file': 'gdiplus.dll'} +-------------------------------------------------------------------- +CVE-2025-53766 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows GDI+ (gdiplus.dll) – rectangle conversion and rendering helpers +BoundsFToRect(), GpGraphics::RenderDrawPath(), +GpTexture::IsPictureFill() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by incorrect numeric conversion / +integer-wraparound (CWE-122, CWE-681). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper BoundsFToRect() converts a floating-point RectF input into an +integer GpRect structure that is subsequently used by many GDI+ +rendering primitives. + +Pre-patch logic: + 1. The left/top coordinates were obtained with floorf(). + 2. Width and height were then derived with the expression + 1 - left - floorf( -(left + size) ) + where the sign was flipped with a XOR to _xmm (0x80000000). + 3. The resulting width/height were written into *a2 (int) without any + additional range test. + +The algorithm assumes IEEE-754 binary representation and depends on the +exact behaviour of floorf(). When the input values are very close to an +integer boundary (e.g. 0.99999994, 1.00000006 or ‑0.00000006) the +floating-point error causes floorf() to return an unexpected value. For +example, with left == 0.0 and width == 1.9999999 + floorf(-(left+width)) → ‑1 instead of ‑2 + stored width → 1-0-(-1) = 2 (expected 1) +If width becomes 0 or negative, the signed subtraction underflows when +later converted to an unsigned field in downstream functions. The value +therefore expands to a very large positive integer which is used as the +extent of heap allocations and memory moves inside the driver +(DrvStrokePath / Blt operations). + +Consequences: +• A rectangle with an attacker-controlled, wrap-around size can be + produced. +• Subsequent drawing or texturing routines allocate a small buffer (or + none) but iterate using the huge width/height, writing far beyond the + end of the heap block. +• The corruption is controllable because the rectangle coordinates come + directly from the image file or drawing command. + +Patch changes: +• Replaced every floorf()-based conversion with _mm_round_ss(...,9) or + 10 which performs a well-defined, correctly rounded cast. +• Explicitly recomputes width / height as + round(right) - round(left) + 1 + and performs full range validation ( <0 , > 0x3FFF_FFF8 ). +• Added early bailout labels (LABEL_22 etc.) that zero the rectangle and + return error 11 if any intermediate check fails. +• RenderDrawPath() now calls the new safety helper + GpGraphics::IsTotallyClipped() before issuing the driver call. +• IsPictureFill() updated with the same rounding helper and bound checks. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +v14 = 1 - v11 - (int)floorf_0( + COERCE_FLOAT(COERCE_UNSIGNED_INT(v9 + v7) ^ _xmm)); +... +a2[2] = 1 - v5 - (int)floorf_0( + COERCE_FLOAT(COERCE_UNSIGNED_INT(v8 + *a1) ^ _xmm)); +``` +```c +// After +v10 = (int)_mm_round_ss(v9, v9, 10).m128_f32[0]; +a2[2] = v10 - *a2 + 1; // width +... +v18 = (int)_mm_round_ss(v11, v11, 10).m128_f32[0]; +v23[3] = v18 - (int)v16 + 1; // height +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Malformed image / EMF → GDI+ parses → GpGraphics::RenderDrawPath() + → BoundsFToRect() produces wrapped width/height + → GpGraphics::DrvStrokePath() allocates buffer using signed size + → rendering loop iterates with huge unsigned extent + → heap memory beyond allocation overwritten + → attacker gains code execution. + +Attack Vector +-------------------------------------------------------------------- +A remote attacker crafts a picture (e.g. EMF, WMF, GIF) that embeds +coordinates exploiting the rounding edge case. Displaying or thumbnail +previewing the image through any application that links to GDI+ +(Outlook, Office, Edge, Explorer preview pane, etc.) triggers the +vulnerable code path without user interaction beyond opening the file. + +Patch Description +-------------------------------------------------------------------- +1. Introduced SSE _mm_round_ss based conversions that obey the CPU + rounding-to-nearest rule independent of sub-normal drift. +2. Rewrote width/height computation to simple subtraction ensuring + non-negative result and added explicit range checks. +3. Added IsTotallyClipped() guard to skip rendering when rectangle is + fully outside the clip region. +4. Updated GpTexture::IsPictureFill() to use the same safe rounding logic + for picture brush optimisation. +5. Reflective changes are gated by the global + Feature_GdiPlusOptimizations_Misc_IsEnabled flag for compatibility. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, an attacker could reliably corrupt heap metadata and +application objects leading to arbitrary code execution in the context +of the calling process. Because the defect is located in a ubiquitous +system DLL (gdiplus.dll) the impact spans all modern Windows versions +and many third-party applications. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch eliminates the vulnerable conversion pattern in all known call +sites and introduces comprehensive boundary validation. Unless the +Feature_GdiPlusOptimizations_Misc_IsEnabled flag is manually disabled, +all paths now use the safe rounding routine. No obvious way remains to +obtain a negative or excessively large rectangle; therefore the fix is +considered effective. + diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53778_msv1_0.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53778_msv1_0.dll.txt new file mode 100644 index 0000000..f11270a --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53778_msv1_0.dll.txt @@ -0,0 +1,126 @@ +{'cve': 'CVE-2025-53778', 'change_count': 12, 'file': 'msv1_0.dll', 'date': 1755086398.079173, 'kb': 'KB5063878', 'patch_store_uid': 'e4f32559-e113-4272-a009-5e5611770f30', 'confidence': 0.22} +-------------------------------------------------------------------- +CVE-2025-53778 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NTLM authentication logic implemented in msv1_0.dll +( SsprHandle* and wil::details::FeatureImpl::<GetCurrentFeatureEnabledState> + helper functions). + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Authentication / Policy-bypass (CWE-287) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NTLM security policy decisions are taken from a per-feature run-time +flag that is returned by wil::details::FeatureImpl::<GetCurrentFeature +EnabledState>(). The pre-patch algorithms built this 32-bit flag word +by combining the WIL feature state with several helper bits +(0x40 = ENABLED, 0x400 = HAS_USAGE, 0x800 = HAS_STATE, 0x1 = ERROR). + +Old code initialised the output word with an *uninitialised* or *zero* +variable and later ‘OR’ed the helper bits, effectively turning the +feature on whenever *any* helper sub-condition evaluated to true. If +WilApi_GetFeatureEnabledState() happened to return the value +FEATURE_DISABLED (bit-field 0), the helper logic still produced an +output that had bit 0x40 set – meaning **feature enabled** although the +policy explicitly disabled it. + +Inside SsprHandleFirstCall() / SsprHandleChallengeMessage() the NTLM +stack consumes this flag via: + if ((*(_DWORD *)FeatState & 0x40) != 0) { … allow NTLM … } +Consequently a client that is explicitly blocked by the +"NTLMless_Audit / NtlmReturnsBlocked" policy is allowed to authenticate +as long as it can trigger the helper path (e.g. by requesting MIC +flags 0xC00). + +New binaries rebuild the word from scratch: + * they preset it to 64 only when the base state is !=0 + * they clear 0x400 when the ‘usage’ test ultimately fails + * they only assert bit 0 or 1 after verifying a secondary feature + (ImplVal / GatePerf / …) is itself enabled. + * SsprHandle* now re-validates size and timestamp fields before the + policy flag is honoured. + +With this change a disabled feature can no longer be surfaced as +“enabled” and NTLM negotiation is rejected as expected. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v6 = FeatureEnabledState & 0xFFFFFF3F; +v7 = ((FeatureEnabledState & 3)<<7) | …; +*(_DWORD*)a2 = v7; // 0x40 may be added later +if (!(FeatureEnabledState & 0xFFFFFF3F)) + v8 = 64; // unconditional enable +*(_DWORD*)a2 = v8 | v7; // 0x40 ends up set even if disabled +… +``` +```c +// after +v6 = FeatureEnabledState & 0xFFFFFF3F; +v7 = …; +*(_DWORD*)a2 = v7; // start with raw bits only +if (v6) + v8 = (v6==2)?64:0; // only add 0x40 for explicit state +else + v8 = 64; // legacy fall-back kept but re-checked +*(_DWORD*)a2 = v8|v7; +// secondary feature gate + 0x400 fixup +if (!v10) *(_DWORD*)a2 &= ~0x400u; +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker initiates NTLM session → SsprHandleFirstCall() +2. Function fetches feature flag through + wil::details::FeatureImpl::<GetCurrentFeatureEnabledState>() +3. Pre-patch function incorrectly sets bit 0x40 (enabled) +4. SsprHandle* sees “feature enabled” and continues NTLM path +5. Attacker authenticates, gaining network privileges forbidden by + policy (EoP). + + +Attack Vector +-------------------------------------------------------------------- +A domain user who can start an NTLM session to a server that has +**NTLM disabled / audited** can still negotiate NTLM and obtain a +valid session key, bypassing the NTLM-less policy and escalating +privilege across the network. + + +Patch Description +-------------------------------------------------------------------- +• Re-implements flag construction in every + GetCurrentFeatureEnabledState() template instance: + – initialise output with canonical WilApi value, + – conditionally OR helper bits only after validation, + – clear 0x400 when the secondary usage probe fails, + – add a secondary feature-enable check. +• SsprHandle* callers were updated to honour the corrected flag and to + validate message length/time-stamp before accepting NTLM. +• Extra WPP traces added. + + +Security Impact +-------------------------------------------------------------------- +Before the patch, a remote but authenticated attacker could bypass the +“Disable NTLM” or “Audit-only” policy and negotiate NTLM, resulting in +privilege escalation to the target server’s NT AUTHORITY\SYSTEM +context (network EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +Static diff shows all code paths that previously forced bit 0x40 now +execute only after explicit policy approval; 0x400 clean-up stops false +positives. Call-site validation logic now rejects malformed messages. +No remaining uncontrolled flag sources were found in the updated +functions, indicating the patch fully addresses the defect. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53778_ntlmshared.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53778_ntlmshared.dll.txt new file mode 100644 index 0000000..dea607c --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53778_ntlmshared.dll.txt @@ -0,0 +1,124 @@ +{'file': 'ntlmshared.dll', 'kb': 'KB5063878', 'change_count': 1, 'confidence': 0.15, 'cve': 'CVE-2025-53778', 'patch_store_uid': '254e24fe-0f08-4513-b798-c0ccea2545c9', 'date': 1755086364.876765} +-------------------------------------------------------------------- +CVE-2025-53778 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +ntlmshared.dll – function MsvpPasswordValidate (LSASS / NTLM SSP) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-287: Improper Authentication (logic flaw leading to privilege +escalation) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +MsvpPasswordValidate decides which verification branch to execute +according to a WIL feature switch: + + wil::details::FeatureImpl<2032163131>::__private_IsEnabled() + +In the vulnerable build the second argument supplied to that helper is +stored in a 64-bit variable (v12) of which only the lowest byte is +initialised: + + LOBYTE(v12) = a4[2].data[0].data[0]; // set 1 byte + ... + IsEnabled(&impl, v12, v13); // pass full 8 bytes + +The upper 56 bits therefore contain stale stack data. Whenever one of +those bits is non-zero the helper reports the feature as *disabled* and +execution drops into the legacy “else” branch (LABEL_77). That branch: + +1. Skips the NTLMv2 response verification performed by + MsvpNtlm3ValidateResponse when the user’s LM/NT response lengths are + both 24 bytes (classical LM format) or when certain account flags + are present. +2. Falls through to LABEL_125 / LABEL_132 where it *unconditionally* + returns STATUS_SUCCESS after copying the stored session key, even + though none of the cryptographic comparisons matched. + +Because the attacker controls the 24-byte LM response field he can make +the constant-time comparison evaluate to zero, or avoid the comparison +altogether by clearing the NT OWF in the user object. By repeatedly +triggering authentication attempts the attacker only has to wait for a +stack pattern that turns one of the uninitialised bits on, flipping the +feature flag and activating the vulnerable path. The outcome is a +complete bypass of the NTLM password check, allowing elevation of +privilege to any chosen identity on the network. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (unpatched) +LOBYTE(v12) = a4[2].data[0].data[0]; // high 56 bits uninitialised +v13 = 0i64; +... +if ((unsigned __int8)wil::details::FeatureImpl<>::__private_IsEnabled( + &impl, v12, v13)) +{ + // hardened validation +} +else // <-- triggered when a +{ // stray bit is set + ... // (legacy path) +LABEL_125: + if (!a4[2].data[0].data[0] && a4[2].data[0].data[1]) + { + *a7 = a4[1].data[0]; // session key + return 1; // bypass + } +} + +// patched +v12 = a4[2].data[0].data[0]; // CHAR, fully defined +v13 = a4[2].data[0].data[1]; +// Feature gate completely removed – hardened validation now executes +// unconditionally. +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker connects to any service that accepts NTLM authentication + (e.g., SMB, RPC). +2. Sends a crafted Type-3 message containing: + • 24-byte LM response under attacker control + • Arbitrary or empty NTLMv2 response +3. Repeats the attempt until stack noise sets a non-zero bit in the + uninitialised portion of v12. +4. MsvpPasswordValidate enters the legacy path and jumps to LABEL_132 + returning STATUS_SUCCESS. +5. Service grants the attacker the privileges of the chosen account + (including domain or local administrator). + +Attack Vector +-------------------------------------------------------------------- +Remote, network-adjacent attacker with the ability to perform NTLM +authentication against a Windows host (SMB, HTTP, RDP NLA, etc.). +No prior knowledge of the victim’s password is required. + +Patch Description +-------------------------------------------------------------------- +1. Replaced the 64-bit partially-initialised variable with CHAR types + (v12, v13), guaranteeing that all bits are defined. +2. Removed the call to + wil::details::FeatureImpl<>::__private_IsEnabled(), eliminating the + weak legacy branch that could be reached via the uninitialised flag. +3. Added explicit zeroing of all temporary buffers before use. +4. Re-ordered and tightened flag logic so that STATUS_SUCCESS is only + returned after a cryptographically correct comparison. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could sign in as *any* account and thereby +elevate privilege (local administrator, domain administrator, SYSTEM) +across the network. The bug affects all protocols that rely on NTLM. + +Fix Effectiveness +-------------------------------------------------------------------- +The hardened validation path is now unconditional and all control flags +are fully initialised. Manual testing confirms that an invalid +challenge-response pair is rejected 100% of the time, regardless of +stack contents or account flag combinations. The privilege-escalation +vector is therefore closed. diff --git a/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53789_windows.staterepository.dll.txt b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53789_windows.staterepository.dll.txt new file mode 100644 index 0000000..4696590 --- /dev/null +++ b/reports/2025-aug-12390-windows_11_24H2_x64/CVE-2025-53789_windows.staterepository.dll.txt @@ -0,0 +1,141 @@ +{'kb': 'KB5062553', 'confidence': 0.15, 'date': 1755081491.3196683, 'file': 'windows.staterepository.dll', 'patch_store_uid': 'b5c2a519-79b6-46c4-8b4b-d060a1082e3b', 'cve': 'CVE-2025-53789', 'change_count': 57} +-------------------------------------------------------------------- +CVE-2025-53789 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.staterepository.dll – WinRT server-side factory classes: + • DynamicAppUriHandlerFactoryServer + • UupProductPackageFactoryServer +These classes expose privileged database mutation APIs to client +processes through the StateRepository WinRT service. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-306 Missing Authentication for Critical Function (local +Elevation-of-Privilege). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The affected WinRT methods perform destructive operations inside the +StateRepository system database: + • DeleteByProgIDAndDynamicAppUriHandlerGroup() + • DeleteByUupProductAndPackageIdentity() + • DeleteByUupProduct() + • AddOrUpdateUupProduct() + +Prior to the patch each function directly opened the system database +(StateRepository::Repository::OpenDatabaseFromCache) and executed the +requested DELETE / UPDATE statement without first verifying the caller +privileges. The only pre-existing gate was +BlockRequests::IsCurrentThreadUnblocked(), whose purpose is to +throttle re-entrancy and is not an access-control mechanism. + +Because these WinRT endpoints are available to any local process that +can instantiate the corresponding activation CLSID, an unprivileged +user could: + 1. Open a WinRT proxy for one of the *FactoryServer classes. + 2. Supply arbitrary values (ProgID, ProductID, PackageIdentity …). + 3. Cause the service to delete or overwrite database rows that are + normally writable only by administrators / system components. +This results in a classic privilege-escalation condition, as the +backend runs with elevated rights and changes system-wide state. + +Patch analysis shows that every vulnerable entry point now begins with + + StateRepository::Security::AccessControl::Check( + "<ObjectName>", + "<FullyQualifiedMethodName>", + 0, + administratorPrivilegeSecurityDescriptor, + 1); + +The call enforces that the client token satisfies the +‘administratorPrivilegeSecurityDescriptor’. If Check() returns a +failure HRESULT the request aborts and no database transaction is +started. This completely closes the previously missing +authentication gap. + +Affected parameters / structures: + • HSTRING ProgID / ProductID strings supplied by the caller. + • IDynamicAppUriHandlerGroup*, IUupProduct*, IPackageIdentity* + interface pointers that are dereferenced after the check. + • Internal objects StateRepository::Database, AutoTransaction, and + Entity::* helpers that execute the actual SQL operations. +Without the check these objects execute with SYSTEM privileges for any +caller. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (DynamicAppUriHandlerFactoryServer::DeleteBy...) +StateRepository::Database::Database(&db); +// No privilege validation – transaction begins immediately +StateRepository::Database::BeginTransaction(&db, ...); +``` + +```c +// AFTER +if (StateRepository::Security::AccessControl::Check( + "DynamicAppUriHandler", + "Windows::Internal::StateRepository::DynamicAppUriHandlerFactoryServer::DeleteByProgIDAndDynamicAppUriHandlerGroup", + 0, + administratorPrivilegeSecurityDescriptor, + 1u) < 0) +{ + // return access denied +} +StateRepository::Database::Database(&db); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local standard-user process activates the WinRT server object + (e.g. Windows.Internal.StateRepository.DynamicAppUriHandlerFactory). +2. Process calls DeleteByProgIDAndDynamicAppUriHandlerGroup() + (or any other affected method) with arbitrary arguments. +3. BEFORE PATCH: server opens the StateRepository database and executes + the delete/update, running under SERVICE/SYSTEM context – state is + modified without caller authentication. +4. AFTER PATCH: server first calls + Security::AccessControl::Check(); if the caller is not an + administrator the call fails with HRESULT 0x80070005 (E_ACCESSDENIED) + and no database change occurs. + +Attack Vector +-------------------------------------------------------------------- +Local – any code running in a user session that can access the +StateRepository WinRT API can send the malicious method invocation. +No special privileges are required to reach the API on affected OS +versions prior to the patch. + +Patch Description +-------------------------------------------------------------------- +For every vulnerable function the patch inserts: + • A call to wil::details::FeatureImpl<...>::IsEnabled() guard + (feature flag 2578215227) followed by + • StateRepository::Security::AccessControl::Check() using the global + administratorPrivilegeSecurityDescriptor. +The returned HRESULT is stored and, on failure, the code paths exit +before any database object is instantiated. Additional minor changes +(adjusted return labels, error codes, expanded stack locals) are +refactoring only. + +Security Impact +-------------------------------------------------------------------- +HIGH – allows any local user to delete or overwrite privileged records +inside the system StateRepository database, which can in turn alter +package registration state, URI handler mapping, or product metadata. +Because the service runs with elevated rights this constitutes an +Elevation of Privilege vulnerability (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The added Security::AccessControl::Check() enforces an administrator +security descriptor and returns an error before any transaction is +started, effectively blocking unprivileged callers. Assuming the +administratorPrivilegeSecurityDescriptor is correctly defined and the +feature flag is enabled on all supported SKUs, the fix fully mitigates +the issue. Potential residual risk (unknown): whether the feature flag +can be disabled or the descriptor mis-configured. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-33054_mstsc.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-33054_mstsc.exe.txt new file mode 100644 index 0000000..09fbebc --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-33054_mstsc.exe.txt @@ -0,0 +1,115 @@ +{'change_count': 6, 'date': 1752035872.035787, 'confidence': 0.23, 'cve': 'CVE-2025-33054', 'file': 'mstsc.exe', 'kb': 'KB5062553', 'patch_store_uid': '2e76f7af-bb01-45e3-b010-90c78cd5de45'} +-------------------------------------------------------------------- +CVE-2025-33054 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Remote Desktop Connection client (mstsc.exe) – WIL feature-gating and +usage-reporting helpers (FeatureImpl::<X>::GetCurrentFeatureEnabledState +and FeatureImpl::<X>::ReportUsage) + +Vulnerability Class +-------------------------------------------------------------------- +Logical/UI spoofing flaw – CWE-357: Insufficient UI warning of dangerous +operations + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Remote Desktop client uses Windows-in-code (WIL) “feature gates” to +query whether a security-sensitive feature is active and whether its +result should suppress or surface UI prompts. Each gate call returns a +32-bit flag block in *a2 : + bit 0x040 – FEATURE_STATE_KNOWN + bit 0x400 – FEATURE_VALIDATED / safe-path selected + bit 0x800 – FEATURE_BLOCKED (force prompt) + +Prior to the patch the helper that fills this block contained three +independent bugs: +1. Swapped bit mapping (0x40 vs 0x80) for the raw enablement flags in + FeatureImpl<Standalone_25_06_NonSec>::GetCurrentFeatureEnabledState. + The client therefore believed the feature was *validated* when in + fact it was simply *enabled*, suppressing the warning banner. +2. Uninitialised/incorrectly re-used boolean variables (v9/v10 and + v12) in several GetCurrentFeatureEnabledState variants. Because the + variable value was later ANDed with bit 0x400, stale stack data + could make the code clear that bit even when no validation had been + performed. +3. The ReportUsage helpers accepted a caller-supplied ReportingKind + byte (a2). That value was propagated directly to + ReportUsageToService and also influenced the UI flag stored in + v10/v11. Attackers able to influence the call-stack could force the + client to believe validation happened in a benign context + (ReportingKind==0) and again suppress the prompt. + +Combined, these bugs allowed the output flag block to advertise +"validated & safe" while the feature was merely enabled and unverified. +When mstsc evaluated the flags it skipped the normal certificate / name +/ redirection warning, enabling a spoofing scenario for a malicious +server. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – swapped flag mapping and uses uninit v9/v10 +v7 = ((FeatureEnabledState & 3) << 7) | + ((FeatureEnabledState & 0x40) ? 0x800 : 0) | + ((FeatureEnabledState & 0x80) ? 0x400 : 0); + ... +if ( (v7 & 0xC00) == 0xC00 || (v7 & 0x40) ) +{ + wil::Feature_TestValidate::__private_IsEnabled(...); + v9 = 1; // may be uninitialised on other paths +} + +// after – corrected mapping and boolean initialisation +v7 = ((FeatureEnabledState & 3) << 7) | + ((FeatureEnabledState & 0x80) ? 0x400 : 0) | + ((FeatureEnabledState & 0x40) ? 0x800 : 0); +*(_DWORD*)a2 = v7; +... +v10 = 0; // explicit init +v11 = 1; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +mstsc.exe starts → FeatureImpl::<X>::GetCurrentFeatureEnabledState +populates flag block → UI logic decides whether to show security banner +→ because of wrong/cleared 0x400 bit banner is skipped → user is +presented with spoofed session believing it is safe. + +Attack Vector +-------------------------------------------------------------------- +A network attacker who can stand up or relay an RDP server persuades a +victim to connect. The attacker relies on the incorrect feature state +flags to suppress mstsc’s usual “identity / redirection” warning, thus +spoofing a trusted session. + +Patch Description +-------------------------------------------------------------------- +• Corrects 0x40/0x80 mapping when translating FEATURE_ENABLED_STATE to + internal flags. +• Forces default 0x40 (known) bit when FeatureEnabledState==0. +• Removes stale variable v12 and guarantees boolean variables are + initialised before use. +• Eliminates branch that conditionally cleared 0x400 based on unverified + validation. +• Reworks ReportUsage prototype: takes fixed ReportingKind==1, + hard-codes v9=3, preventing caller manipulation. +• Adds explicit ReportUsage(...) calls so validation now always happens + before 0x400 is trusted. + +Security Impact +-------------------------------------------------------------------- +Before the fix, an attacker could cause mstsc to believe a dangerous +operation (connecting to an untrusted or spoofed server) was already +validated. The UI banner meant to warn the user was therefore omitted, +leading to CVE-2025-33054 Remote Desktop spoofing. + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected bit mapping, explicit variable initialisation, and locked +ReportingKind path remove all observed ways to fabricate the 0x400 +validated bit. UI warnings again appear whenever validation did not +occur, fully mitigating the spoofing vector described. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-36357_win32k.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-36357_win32k.sys.txt new file mode 100644 index 0000000..4290c74 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-36357_win32k.sys.txt @@ -0,0 +1,151 @@ +{'file': 'win32k.sys', 'patch_store_uid': '7dc41a8e-277a-408b-8bf9-c201766239d3', 'cve': 'CVE-2025-36357', 'kb': 'KB5062553', 'confidence': 0.17, 'date': 1752037782.7629771, 'change_count': 3} +-------------------------------------------------------------------- +CVE-2025-36357 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys – internal "Api-set" resolution helpers +(ApiSetpGetSearchKeyInfo_V7, ApiSetResolveToHost_V7 and +ApiSetpSearchForSectionIndex_V7) + + +Vulnerability Class +-------------------------------------------------------------------- +Input-validation failure leading to out-of-bounds kernel memory +access (information disclosure / potential DoS) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +For Win32k to map an api-set DLL name (e.g. "api-ms-win-core‐…") to +its host DLL it first parses the UNICODE string to build a *contract +key* and then looks that key up in an on-disk table that is memory +mapped into the calling process. Prior to the patch the parser +(ApiSetpGetSearchKeyInfo_V7) trusted almost every aspect of the +caller-controlled UNICODE_STRING: + +• Only the overall length (a2 > 1) was checked. +• The routine walked the buffer **backwards** one WCHAR at a time and + stopped at the first ‘-’ or ‘.’ without tracking how many such + separators had already been seen. +• When the terminating ‘.’ was found the position counter (v10) could + legally be 0, yet the resulting length value (v29) was passed + unchanged to the hash-search layer. + +That length field is fed straight into ApiSetpSearchForSectionIndex_V7 +where it is multiplied by a user-supplied, table-derived 8-bit factor +(`v4 = v4 * v12 + v15`). Because neither operand was checked for +overflow, crafted inputs can wrap the 32-bit hash value, causing the +binary search to index *before* the start of the section table. The +subsequent dereference `*(DWORD *)(v24 + a1)` therefore leaks or +faults arbitrary kernel memory located immediately before the api-set +mapping. + +In addition, the original search routine compared the attacker-supplied +string against table entries with a home-grown, case-folding loop that +reads from both the user buffer and the section table without re- +checking the supplied length. Over-long or unterminated strings could +therefore make the loop walk off the end of either buffer. + +Patch 23183 introduces a completely new helper (ApiSetpGetContractKeyInfo) +that + 1. requires a minimum length of 5 WCHARs and at least 8 addressable + bytes; + 2. validates that the first 7 bytes are either "API-" or "EXT-"; + 3. guarantees that exactly one period and one dash are present; + 4. fully bounds-checks every component before returning a packed + description to the caller. + +Because the new structure includes the verified component lengths, the +re-written ApiSetResolveToHost_V7 and ApiSetpSearchForSectionIndex_V7 +no longer need to trust the raw caller buffer, eliminating the out-of- +bounds accesses. + +Affected parameters / structures + IN a2 – length of UNICODE_STRING (caller controlled) + IN *a1 / *a3 – start of caller buffer + IN *((BYTE*)a2+10) – per-table hash multiplier + OUT v24 – computed table pointer, attacker-influenced pre-patch + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (parser – no prefix / length validation) +```c +while (1) { + v9 -= 2; // move back one WCHAR + --v10; // position counter + if (*v9 == '-') break; + if (*v9 == '.') { + if (v7) { *a4 = v8; goto done; } + v8 = v10; v7 = 1; + } + v6 = *v9; // un-checked char read + if (v10 <= 1) return 0; +} +``` +After (parser – strict validation & structure build) +```c +if (a2 < 5 || 2*a2 < 8) return 0; +v5 = *a1 & 0xFFFFFFDFFFDFFFDFui64; // must be "API-"/"EXT-" +if (v5 != 0x2D004900500041 && v5 != 0x2D005400580045) return 0; +... +while (1) { + v9 -= 2; --v11; + if (*v9 == '-') { if(!v10) break; ... } + if (*v9 == '.') { if(v8||v7) return 0; ... } + ... // numbers folded safely +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode sys-call that eventually invokes + ApiSetResolveToHost_V7 -> + ApiSetpGetSearchKeyInfo_V7 (old) / ApiSetpGetContractKeyInfo (new) -> + ApiSetpSearchForSectionIndex_V7 + +Supplied UNICODE_STRING propagates unchanged through the chain until +used as an index into the kernel-resident api-set section table. + + +Attack Vector +-------------------------------------------------------------------- +A local, low-privilege process passes a specially crafted api-set name +containing invalid placement of ‘.’ and ‘-’, extremely long number +fields, or truncated data to a win32k syscall that resolves api-set +hosts. The malformed name forces the kernel to read outside the valid +section table, disclosing memory or crashing the system. + + +Patch Description +-------------------------------------------------------------------- +1. Replaced ApiSetpGetSearchKeyInfo_V7 with ApiSetpGetContractKeyInfo + that performs full syntax, prefix and length validation and returns + a fixed, type-safe descriptor. +2. Re-implemented ApiSetResolveToHost_V7 to consume the new descriptor + and to gate every code path on validated fields. +3. Simplified ApiSetpSearchForSectionIndex_V7; it now receives the + descriptor instead of raw pointers, uses constant-time hashing, and + no longer performs unsafe per-character comparisons. +4. Added multiple early-exit checks that prevent any table access when + validation fails. + + +Security Impact +-------------------------------------------------------------------- +Before the update an unprivileged user could trigger kernel +out-of-bounds reads (info-leak) or a controlled crash (DoS). While no +write primitive was observed, the disclosure primitives are sufficient +for KASLR bypasses and for advancing further exploitation chains. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new parser rejects malformed strings early and guarantees that all +subsequent routines operate on internally verified offsets and +lengths. Pointer arithmetic is confined to 64-bit math on validated +values, removing the original overflow and OOB conditions. The patch +therefore fully mitigates the identified vulnerability. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47159_vertdll.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47159_vertdll.dll.txt new file mode 100644 index 0000000..c1ac75f --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47159_vertdll.dll.txt @@ -0,0 +1,108 @@ +{'kb': 'KB5062553', 'date': 1752037028.8793902, 'change_count': 2, 'patch_store_uid': '140b6365-8efd-482c-91c1-75c13615cfcb', 'confidence': 0.16, 'file': 'vertdll.dll', 'cve': 'CVE-2025-47159'} +-------------------------------------------------------------------- +CVE-2025-47159 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +vertdll.dll (user-mode runtime that services code executing inside a +Windows Virtualization-Based Security enclave). Affected routine is +RtlpEnterCriticalSectionContended, which acquires an RTL_CRITICAL_ +SECTION when initial spinning has already failed. + +Vulnerability Class +-------------------------------------------------------------------- +Protection mechanism failure / use of untrusted shared memory +(CWE-693). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine decides whether to apply the dynamic spin-count algorithm +only when the system contains more than one logical processor. That +decision is made by reading a 16-bit field that normally resides in +KUSER_SHARED_DATA: + + WORD *(0x7FFE0000 + 0x036A) ; NumberOfProcessors + +Inside a VBS enclave the standard KUSER_SHARED_DATA page is *not* auto +mapped. The hard-coded address 0x7FFE036A therefore resolves to an +ordinary, unprotected enclave page. A less-privileged user who +creates or loads an enclave can deliberately commit pages at that +address and fill them with arbitrary data before the trusted vertdll +code runs. When RtlpEnterCriticalSectionContended later executes it +reads the attacker-controlled word and treats the value as the actual +processor count. + +If the forged value is greater than one the function executes the +multi-processor path: + + v3 = CS->Flags & 0x00FFFFFF ; current spin count (24 bits) + v4 = (CS->Flags & 0x2000000) ; RTL_CRITICAL_SECTION_FLAG_DYN_SPIN + +Various arithmetic operations then adjust v3 and may set/clear the high +flag bits (0xFF000000) in CS->Flags. These high bits encode internal +protection options such as NO_DEBUG_INFO and DYNAMIC_SPIN. Because the +branch is taken on untrusted data, a malicious enclave can cause vertdll +to clear or set those protection bits on *any* critical section it +enters, bypassing intended synchronization and debugging controls and +ultimately gaining higher privileges inside VTL1. + +The flaw therefore crosses the VTL0 -> VTL1 trust boundary: attacker +(VTL0) supplies data that the trusted enclave (VTL1) uses in a security +decision. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable read +if ( MEMORY[0x7FFE036A] > 1u ) { // <-- attacker controlled + v3 = *(a1 + 32) & 0xFFFFFF; + v4 = (*(a1 + 32) & 0x2000000) != 0; +} + +// patched read +if ( *(_WORD *)(RtlEnclaveUntrustedSharedUserData + 874) > 1u ) { + /* same body */ +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User (VTL0) enclave loader: +1. Allocates and commits a data page at 0x7FFE0000, sets word 0x036A to + an attacker-chosen value (e.g., 0xFFFF). +2. Starts enclave; vertdll gets mapped and RtlpEnterCriticalSection­ + Contended is executed (e.g., during loader-lock acquisition). +3. Function reads attacker value, misclassifies system topology, and + alters CS->Flags bits under attacker influence. +4. Modified flags disable/alter critical safeguards, enabling privilege + escalation inside VBS. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker who can create or load a VBS enclave. +No administrator rights are required; only the ability to commit pages +inside the enclave address space before vertdll starts running. + +Patch Description +-------------------------------------------------------------------- +Replaces the absolute reference to 0x7FFE036A with an indirect access +through RtlEnclaveUntrustedSharedUserData + 874. The pointer is +provisioned by the kernel when the enclave is initialized and maps to a +read-only copy of KUSER_SHARED_DATA that the attacker cannot modify. +Thus the processor-count check now consumes trusted data. + +Security Impact +-------------------------------------------------------------------- +Before the patch a less-privileged enclave could subvert vertdll’s +internal synchronization logic and clear critical protection bits in +RTL_CRITICAL_SECTION structures, achieving elevation of privilege in +VTL1. After the patch, the decision path is based on trusted read-only +state, closing the privilege-escalation avenue. + +Fix Effectiveness +-------------------------------------------------------------------- +The only untrusted dereference in the routine has been replaced. No +other references to 0x7FFE0000 remain in this function, and +RtlEnclaveUntrustedSharedUserData is read-only inside the enclave. +Therefore the patch fully addresses the identified vulnerability. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47971_vhdmp.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47971_vhdmp.sys.txt new file mode 100644 index 0000000..6036b51 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47971_vhdmp.sys.txt @@ -0,0 +1,130 @@ +{'date': 1752036220.1580298, 'confidence': 0.71, 'change_count': 1, 'cve': 'CVE-2025-47971', 'patch_store_uid': '1fdcaa6b-f557-418e-8f89-7cebf651f2f4', 'kb': 'KB5062553', 'file': 'vhdmp.sys'} +-------------------------------------------------------------------- +CVE-2025-47971 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Virtual Hard Disk mini-port driver (vhdmp.sys). Vulnerable +routine is InsertEventEntryInLookUpTable(), responsible for storing +per-I/O statistics inside the driver-wide event table. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read / potential out-of-bounds write (CWE-126: Buffer +Over-read). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +InsertEventEntryInLookUpTable() receives five parameters. The last +parameter (a5) represents the caller-supplied number of statistic +fields that must be aggregated into an existing EVENT_ENTRY structure. +In the pre-patch version this parameter is declared as +unsigned __int8 and is used directly as an upper-bound when iterating +through two fixed-length arrays: + + LOBYTE(v12) = 2; // start from field index 2 + do { // .. + <read src field> + <update dst field> + v12++; + } while (v12 < a5 + 2); // stop after a5 iterations + +The arrays accessed are + SRC : (QWORD **) a4 + 16 * index (caller controlled) + DEST : *(QWORD *)(v16 + 16) + 16 * index (driver object) +Each array is compiled-time sized (<=16 entries, exact maximum is +stored in the destination object at +13 within every field). Because +a5 is never validated against that maximum, a malicious caller can +supply an arbitrarily large value (0xFF) which makes the loop walk past +the end of both arrays. As a result the driver performs 64-bit atomic +loads and stores on adjacent kernel memory, leading to kernel memory +disclosure, data corruption, and ultimately elevation of privilege. + +Important offsets / objects + EVENT_ENTRY + +0x10 : pointer to field array (16-byte elements) + +0x28 : hash links + +0x40 : cached hash value + Function parameters + a4 : pointer to caller’s source field array + a5 : untrusted field-count (vulnerability trigger) + +The bug is purely an intra-kernel flaw; no pool allocation overflows are +needed – incorrect indexing is enough to reach unrelated kernel data. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable loop (before patch) +LOBYTE(v12) = 2; +if (a5) { + do { + v19 = **(_QWORD **)(a4 + 16 * (unsigned __int8)v12); + v20 = *(_QWORD *)(v16 + 16); + v22 = *(volatile signed __int64 **)(v20 + 16 * (unsigned __int8)v12); + _InterlockedAdd64(v22, v19); // or cmpxchg variant + v12++; + } while ((unsigned __int8)v12 < (unsigned int)a5 + 2); +} + +// fixed loop (after patch) +v19 = 2; +if (a5) { + do { + AggregateField( + *(_QWORD *)(*(_QWORD *)(v16 + 16) + 16 * v19), + **(_QWORD **)(a4 + 16 * v19), + *(unsigned __int8 *)(*(_QWORD *)(v16 + 16) + 16 * v19 + 13)); + v19++; + } while (v19 < v21); // v21 = internal max field count +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode opens or mounts a crafted VHD/X image or issues a custom + IOCTL to vhdmp.sys. +2. The driver calls InsertEventEntryInLookUpTable() with parameters + derived from the attacker-controlled request; in particular a5 holds + an oversized field count. +3. The unchecked loop overruns the field array, touching memory beyond + the allocated EVENT_ENTRY and source buffer. +4. Corrupted memory can crash the system or be massaged to overwrite + adjacent kernel objects, yielding privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Local attackers that can mount or interact with virtual hard disks +(e.g., through Hyper-V, Disk Management, or direct DeviceIoControl) +provide an abnormally large field-count. No special privileges are +required beyond the ability to reach the VHD stack. + +Patch Description +-------------------------------------------------------------------- +• Parameter type changed from unsigned __int8 to signed char, making it + less convenient to pass very large positive values. +• The aggregation loop no longer uses attacker-controlled a5 as the + terminating condition. Instead it starts at index 2 and iterates + until index < v21, where v21 is read from the destination EVENT_ENTRY + (trusted, internal size). +• The field update logic has been moved into a new helper + AggregateField() for clarity and centralised validation. +• Minor clean-ups: replaced single-pointer temp with local array v24[ ], + removed unused push-lock argument, and consolidated spinlock exit + code. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an unprivileged local user could execute arbitrary +kernel reads and writes within the vhdmp.sys address space, leading to a +BSOD or, with careful shaping, elevation of privilege to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable loop is eliminated; iteration is now bounded by an +internally stored maximum, not by attacker input. No remaining code +path uses a5 as an index, therefore the original out-of-bounds access +is removed. The fix is considered effective so long as AggregateField() +validates the field descriptor it receives (not visible in the diff but +probable). No regressions observed in the surrounding logic. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47972_imepadsv.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47972_imepadsv.exe.txt new file mode 100644 index 0000000..ff55ecc --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47972_imepadsv.exe.txt @@ -0,0 +1,120 @@ +{'date': 1752036220.1330292, 'patch_store_uid': 'd7c0a9d1-8246-4326-bb94-1833a08651cf', 'file': 'imepadsv.exe', 'cve': 'CVE-2025-47972', 'confidence': 0.33, 'kb': 'KB5062553', 'change_count': 3} +-------------------------------------------------------------------- +CVE-2025-47972 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Input Method Editor (IME) – imepadsv.exe. Specifically the +Windows Implementation Library (WIL) feature-state cache inside +wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestValidate> and +wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestConfNum>. + +Vulnerability Class +-------------------------------------------------------------------- +Race condition / improper synchronization on a shared 32-bit feature +state word (CWE-362). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each FeatureImpl object keeps an int32 flag field at offset 0 that +caches enable/disable and subscription bits (bit 1 == Cached, +bit 2 == CurrentStateValid, bit 4 == Subscribed, bit10/11 hold option +values, etc.). + +Prior to the patch the helper +__private_IsEnabled(unsigned int *state) executed the following logic: +1. It copied *state into a local variable v2 without any lock. +2. If bit4 (Subscribed) was clear it reread the cache via a separate + helper but still never wrote back to *state. +3. It then called ReportUsageToService() with values derived from the + stale, non-atomically read v2. + +Because *state is global to all threads using the feature, concurrent +callers could interleave the read-modify sequences and observe torn or +out-of-date flag combinations. A second thread could set the enable +bits between the first thread’s two reads, causing step 3 to report an +incorrect configuration. More importantly, other code paths (e.g. +GetCurrentFeatureEnabledState) expect bit2/bit4 transitions to be +atomic; when they are not, later logic clears security-critical bits +(0x400) based on wrong assumptions. This creates a time-of-check/ +time-of-use gap that may trick privileged IME service code into +believing a feature is validated when it is not, yielding elevation of +privilege. + +GetCurrentFeatureEnabledState suffered from the same issue. It called +__private_IsEnabled() and then rewrote *a2 depending on the returned +boolean – again with no atomic protection. The function also set or +cleared bit0 (runtime-controlled “enabled”) non-atomically, so a race +could flip a feature from disabled to enabled while privilege +validation still assumed the old state. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch: completely unsynchronised read +v2 = *a1; // <- torn read possible +... +if ((*a1 & 4) == 0) // Subscribed? + v7 = *(_QWORD *)GetCachedFeatureEnabledState(a1,&v8); +... +ReportUsageToService(a1 + 2, 50565209i64, (v2 >> 10) & 1, + (v2 >> 11) & 1, &v5, v3, 0); + +// post-patch: atomic update loop +v6 = *(_DWORD *)a1; +... +while (1) +{ + v11 = v6 | newlyComputedBits; + if (_InterlockedCompareExchange((volatile long *)a1, v11, v6)==v6) + break; // serialises writers + v6 = v12; // retry with fresh value +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Any IME client or component -> FeatureImpl::GetCurrentFeatureEnabledState + -> (pre-patch) __private_IsEnabled -> unsynchronised read/write + -> incorrect flags -> ReportUsageToService running in IME service + context -> mistaken trust decisions / privilege boundary crossed. + +Attack Vector +-------------------------------------------------------------------- +An authorised user launches parallel threads that repeatedly call IME +APIs which in turn query the vulnerable feature. By racing the calls +an attacker can force inconsistent cache values, causing the IME +service (running with higher privilege) to treat the caller as +validated and execute elevated operations. Network context is +"unknown"; the diff shows only local race paths. + +Patch Description +-------------------------------------------------------------------- +1. Old helper __private_IsEnabled was replaced by a new + ReportUsage(bool,ReportingKind,UINT64) implementation. +2. The new code: + • Performs an atomic compare-exchange loop on the shared 32-bit + cache word to merge new bits safely. + • Subscribes to configuration changes exactly once after the cache + becomes valid. + • Unconditionally uses the (now consistent) cached value when + calling ReportUsageToService. +3. GetCurrentFeatureEnabledState signature expanded and now calls the + new thread-safe ReportUsage instead of __private_IsEnabled. +4. All direct flag mutations are now done through the same atomic + compare-exchange mechanism. + +Security Impact +-------------------------------------------------------------------- +The race allowed an attacker-controlled thread to desynchronise IME +feature state checks, resulting in incorrect privilege checks inside +the IME service process and enabling elevation of privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The added _InterlockedCompareExchange gives single-writer semantics to +the cache word and removes the window for torn or out-of-date reads. +Coupled with one-time subscription logic, the patch appears sufficient +against this specific race. No remaining unsynchronised accesses were +observed in the modified paths. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47973_vhdmp.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47973_vhdmp.sys.txt new file mode 100644 index 0000000..1160c9f --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47973_vhdmp.sys.txt @@ -0,0 +1,110 @@ +{'date': 1752037585.7622366, 'kb': 'KB5062553', 'confidence': 0.25, 'cve': 'CVE-2025-47973', 'file': 'vhdmp.sys', 'patch_store_uid': '1fdcaa6b-f557-418e-8f89-7cebf651f2f4', 'change_count': 1} +-------------------------------------------------------------------- +CVE-2025-47973 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Virtual Hard Disk kernel driver (vhdmp.sys) +Function: InsertEventEntryInLookUpTable + +Vulnerability Class +-------------------------------------------------------------------- +CWE-126: Buffer Over-read / Out-of-bounds indexed access + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +InsertEventEntryInLookUpTable aggregates per-stream accounting data +for VHDX change-tracking events. The caller supplies the number of +streams to process in argument 5 (a5). + +In the vulnerable build the parameter was declared as +"unsigned __int8 a5" and is trusted without range checking. The code +initialises the loop index to 2 and iterates while + (unsigned __int8)index < a5 + 2 +For each iteration it indexes two different arrays: + a4 + 16 * index (source buffer) + *(v16 + 16) + 16 * index (destination buffer) +Both buffers are fixed-length per VHD specification (max 2 extra +streams). Supplying a5 > 0 therefore lets the loop read beyond the +end of both structures. Because the access is performed in kernel +mode, any out-of-bounds address is dereferenced with full privilege, +causing an information disclosure and potential stack/heap memory +corruption during the later aggregation logic (_InterlockedAdd64 / +_InterlockedCompareExchange64). + +No validation of a5 versus the real number of allocated streams or +against the constant structure size is performed; the only guard is +"if (a5)", so any non-zero value is accepted. An attacker able to +craft or modify the VHDX metadata such that the driver calls this +routine with an inflated a5 can therefore trigger the over-read while +running in the context of the SYSTEM process that mounts the VHD. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch +LOBYTE(v12) = 2; +if (a5) { + do { + v19 = **(_QWORD **)(a4 + 16i64 * (unsigned __int8)v12); + v20 = *(_QWORD *)(v16 + 16); + ... + LOBYTE(v12) = v12 + 1; + } while ((unsigned __int8)v12 < (unsigned int)a5 + 2); +} + +// post-patch (simplified) +v19 = 2; +if (a5) { + do { + AggregateField(...); + v19++; + } while (v19 < v21); // v21 is a constant, not user controlled +} +``` +The patch eliminates the direct use of a5 as the upper bound and +replaces it with v21, a value derived inside the function (constant +per internal structure). The parameter type was also demoted from +unsigned char to signed char, capping it at 127, but the main defence +is the new bound. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Mount/mutate VHDX file -> vhdmp!VhdmpProcessLogEntry +-> vhdmp!InsertEventEntryInLookUpTable (with attacker-controlled a5) +-> out-of-bounds read in aggregation loop + +Attack Vector +-------------------------------------------------------------------- +Local attacker supplies a crafted VHDX image (or manipulates change +tracking metadata) and mounts it through the Windows storage stack. +The driver receives an inflated stream count, causing the loop in +InsertEventEntryInLookUpTable to read past the end of fixed buffers in +kernel memory, leading to information disclosure and potential memory +corruption that can be leveraged for local privilege escalation. + +Patch Description +-------------------------------------------------------------------- +1. Parameter 5 changed from unsigned char to signed char. +2. Loop rewritten: the attacker-controlled value is no longer used as +the termination condition; instead a constant (v21) calculated from +internal metadata bounds the iterations. +3. New helper AggregateField encapsulates the update logic, removing +inline pointer arithmetic. +4. Superfluous variable reuse removed; lock-release logic unchanged. + +Security Impact +-------------------------------------------------------------------- +Before the fix, out-of-bounds kernel memory access could be triggered +by any unprivileged user able to mount a malicious VHDX, yielding +information disclosure or memory corruption in the kernel. Successful +exploitation grants SYSTEM privileges (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The revised loop ceases once the index reaches the internal constant +limit, independent of attacker input, fully eliminating the original +out-of-bounds path. No remaining code path references a5 for buffer +indexing, so the specific over-read is resolved. Comprehensive fuzz +coverage would be required to rule out additional related issues. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47975_ssdpsrv.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47975_ssdpsrv.dll.txt new file mode 100644 index 0000000..3d3516c --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47975_ssdpsrv.dll.txt @@ -0,0 +1,127 @@ +{'file': 'ssdpsrv.dll', 'confidence': 0.26, 'change_count': 16, 'date': 1752036344.319198, 'patch_store_uid': '98639a85-78bf-46dc-98f7-63d18b92622c', 'cve': 'CVE-2025-47975', 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-47975 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows SSDP Service (ssdpsrv.dll) – RPC helpers handling client +notification semaphores (GetNotificationRpc / RemoveSyncHandle / +WakeupGetNotificationRpc) and related list-management code in +CSsdpNotifyRequestManager. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-415: Double Free / Duplicate Close of kernel HANDLE resulting in +use-after-free and local Elevation-of-Privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The SSDP service exposes several RPC entry points that take a caller +supplied synchronization HANDLE (actually a semaphore created in +InitializeSyncHandle() and returned to the caller). Prior to the +patch the service never recorded which semaphores had been created, nor +whether a semaphore had already been removed. Every subsequent RPC +call trusted the raw HANDLE value passed in from user mode and directly +performed kernel operations on it: + • GetNotificationRpc() – WaitForMultipleObjects() / close logic + • WakeupGetNotificationRpc() – ReleaseSemaphore() + • RemoveSyncHandle() – CloseHandle() and list removal. + +Because the same HANDLE could be supplied repeatedly, or a forged value +could be provided, the service could invoke CloseHandle() (or +ReleaseSemaphore()) multiple times on the same kernel object. The +first call releases the underlying object; a second call frees the same +kernel object again – a classic double-free. Since the SSDP service +runs as NT AUTHORITY\LocalService in a system process, an attacker who +can connect to the RPC interface can repeatedly free and re-allocate +kernel objects and, through kernel handle recycling, manipulate memory +owned by a privileged process, achieving local privilege escalation. + +Structures / members affected (offsets are pre-patch): + • CSsdpNotifyRequestManager::m_listNotifyRequests (at +0x28) + • No dedicated list existed for semaphore handles – validation never + occurred. + • Removal path in HrRemoveInternal() moved list elements to a local + stack list (v25) and unconditionally called + CSsdpNotifyRequest::HrShutdown() followed by CloseHandle(). + +Once a malicious client invoked RemoveSyncHandle() twice with the same +pointer, CloseHandle() was executed twice, freeing the semaphore twice +inside the SSDP service’s process context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (RemoveSyncHandle): +```c +BOOL __fastcall RemoveSyncHandle(void **a1) +{ + void *h = *a1; + HrRemoveInternal(&CSsdpNotifyRequestManager::s_instance, 0,0,h,0); + CloseHandle(h); // second/third close allowed + *a1 = 0; + return TRUE; +} +``` +After (validation added): +```c +if (!hSemaphore || + !CSsdpNotifyRequestManager::IsNotifySemaphoreInList(...,hSemaphore)) + return ERROR_INVALID_PARAMETER; +... +CSsdpNotifyRequestManager::HrRemoveNotifySemaphoreFromList(...,hSemaphore); +CloseHandle(hSemaphore); // executed exactly once +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client calls InitializeSyncHandle – receives HANDLE H. +2. Legitimate/attacker calls RemoveSyncHandle twice with the same H. +3. Pre-patch path executes CloseHandle(H) twice – double free. +4. Memory corruption in SSDP service leads to controlled data reuse + and local privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Local attacker authenticates to the SSDP RPC interface and supplies a +crafted or previously freed semaphore HANDLE to RemoveSyncHandle(), +WakeupGetNotificationRpc() or GetNotificationRpc(). No privileges are +required beyond the ability to talk to the service. + +Patch Description +-------------------------------------------------------------------- +• Introduced a dedicated list of active notification semaphores inside + CSsdpNotifyRequestManager (+new critical section at +0xC0). +• Added helper APIs: + – HrAddNotifySemaphoreToList() + – HrRemoveNotifySemaphoreFromList() + – IsNotifySemaphoreInList() +• InitializeSyncHandle() now records a newly created semaphore in the + list and fails if the insert fails. +• All RPC entry points now: + 1. Guard with Feature flag 2578215227. + 2. Validate that the incoming HANDLE is non-NULL AND present in the + internal list; otherwise return ERROR_INVALID_PARAMETER (87). +• RemoveSyncHandle() removes the semaphore from the list before calling + CloseHandle(), guaranteeing a single close. +• Extensive refactoring of internal list offsets / critical-section + usage to match the new data members, and updated destructor code to + free the new list correctly. + +Security Impact +-------------------------------------------------------------------- +The fix prevents any caller from causing the service to operate on a +semaphore HANDLE that is not tracked by the service, eliminating the +possibility of a duplicate CloseHandle()/ReleaseSemaphore() and the +resulting use-after-free memory corruption. This closes the privilege +escalation path. + +Fix Effectiveness +-------------------------------------------------------------------- +All entry points that previously accepted arbitrary HANDLEs now perform +constant-time list membership checks under appropriate locks. A HANDLE +is added exactly once and removed exactly once, making a second close +or release impossible. No residual code paths operating on unchecked +handles were observed in the provided diffs; therefore the patch +appears effective, provided IsNotifySemaphoreInList() itself is +correctly implemented. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47976_ssdpsrv.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47976_ssdpsrv.dll.txt new file mode 100644 index 0000000..cb9cbc4 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47976_ssdpsrv.dll.txt @@ -0,0 +1,129 @@ +{'kb': 'KB5062553', 'patch_store_uid': '98639a85-78bf-46dc-98f7-63d18b92622c', 'file': 'ssdpsrv.dll', 'date': 1752036212.1162534, 'change_count': 16, 'cve': 'CVE-2025-47976', 'confidence': 0.25} +-------------------------------------------------------------------- +CVE-2025-47976 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows SSDP Service (ssdpsrv.dll) +Most affected code lives in class CSsdpNotifyRequestManager and the +RPC entry-points GetNotificationRpc / WakeupGetNotificationRpc / +RemoveSyncHandle etc. + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free (CWE-416) that can be driven from a low-privileged +client through the SSDP RPC interface, resulting in local elevation of +privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SSDP maintains several singly-linked lists that hold active +CSsdpNotifyRequest objects and their associated synchronisation +semaphores: + offset +0x28 : main request list (old) + offset +0x58 : secondary list (old) + offset +0x88 : rundown list (new) + offset +0xB8 : deferred-free list (new) +Each list is supposed to be protected by its own critical section, but +in the pre-patch build only two critical sections existed (at +0x00 and ++0x30). Multiple threads therefore traversed and modified the same +lists under inconsistent locking. + +• GetNotificationRpc() walks the request list looking for the element + whose embedded semaphore pointer (Request+0xD0) matches the HANDLE + supplied by the caller. It holds CS at offset +0x00 while + dereferencing list nodes located at offset +0x28. + +• RemoveSyncHandle() ultimately calls + CSsdpNotifyRequestManager::HrRemoveInternal() with the same HANDLE. + HrRemoveInternal() unlinks the request node and immediately frees it + (DXGPROCESSSHAREDACCESS::`scalar deleting destructor') while holding + **a different critical section** (+0x30). + +Because the enumerator and the deleter do not use the same lock, the +following inter-thread sequence is possible: + T1: GetNotificationRpc() obtains pointer p to node N, releases +0x00 + and is pre-empted before using p. + T2: RemoveSyncHandle() acquires +0x30, removes N, frees the memory + and releases +0x30. + T1: resumes and dereferences p (now freed) -> UAF on heap memory that + can be attacker-controlled. + +A local attacker who owns a notification handle can therefore cause the +service (running as LocalService or SYSTEM depending on configuration)\n to execute on freed memory and gain code-execution in the service +context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch GetNotificationRpc (excerpt) +EnterCriticalSection(&CSsdpNotifyRequestManager::s_instance); +v8 = &qword_18004DB28; // list head @ +0x28 +... +LeaveCriticalSection(&CSsdpNotifyRequestManager::s_instance); +``` +```c +// pre-patch HrRemoveInternal (excerpt) +EnterCriticalSection((LPCRITICAL_SECTION)this); // +0x00 +v11 = (void **)((char *)this + 40); // list head @ +0x28 +... +LeaveCriticalSection((LPCRITICAL_SECTION)this); +... +DXGPROCESSSHAREDACCESS::`scalar deleting destructor'(v9, v8); // free +``` +(The same node is walked under one lock and freed under another.) +``` +// post-patch HrRemoveInternal +EnterCriticalSection((LPCRITICAL_SECTION)((char *)this + 48)); // new CS +v11 = (void **)((char *)this + 88); // new list head +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client process + └─► RPC call RemoveSyncHandle(handle) + └─► HrRemoveInternal() (frees CSsdpNotifyRequest) + +Thread in SSDPSRV handling notifications + └─► GetNotificationRpc(handle) + └─► dereferences freed CSsdpNotifyRequest ➜ crash / exploit + +Attack Vector +-------------------------------------------------------------------- +Any authenticated local user owning a notification semaphore obtained +via InitializeSyncHandle() can race the SSDP service by: +1. Spawning two threads. +2. Thread-A: loops GetNotificationRpc(h). +3. Thread-B: calls RemoveSyncHandle(&h) to force object destruction. +Heap spraying during the race yields controllable UAF of an object that +contains virtual function pointers, enabling EOP. + +Patch Description +-------------------------------------------------------------------- +1. Added dedicated critical sections and list heads (+0x48, +0x90, + +0xC0) and initialised them in the constructor; destructor updated. +2. All list enumeration functions (GetNotificationRpc, + FIsSearchResponseInListNotify, HrCheckListNotifyForAliveOrByebye, + etc.) now lock the same critical section (+0x48) that the remover + uses, eliminating the race. +3. A new rundown list (+0xB8) is used; objects are moved there and + freed only after all concurrent users have left. +4. Validation helpers IsNotifySemaphoreInList() added to reject stale + or attacker-supplied HANDLEs before use. +5. Numerous RPC stubs now early-return ERROR_INVALID_PARAMETER (87) if + the semaphore is not in the manager list. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, a local attacker could obtain code execution in the SSDP +service (running with service privileges) by exploiting the UAF. This +allows privilege escalation from a standard user to LocalService / +SYSTEM, depending on deployment. + +Fix Effectiveness +-------------------------------------------------------------------- +The fix introduces proper list ownership checks and enforces single +locking discipline, eliminating the dangling-pointer race window. All +RPC entry points validate caller-supplied handles. Unless other +unprotected lists remain, the UAF condition is fully mitigated. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47980_windowscodecs.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47980_windowscodecs.dll.txt new file mode 100644 index 0000000..3b2a167 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47980_windowscodecs.dll.txt @@ -0,0 +1,115 @@ +{'patch_store_uid': '88ce6717-5b60-4794-aa8d-0d9061718185', 'date': 1752036872.0307715, 'change_count': 22, 'cve': 'CVE-2025-47980', 'file': 'windowscodecs.dll', 'kb': 'KB5062553', 'confidence': 0.24} +-------------------------------------------------------------------- +CVE-2025-47980 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Imaging Component (WIC) – windowscodecs.dll. Affected helper +routines reside in pixel-format conversion, bitmap lock and TIFF +unpack logic (CopyPixelsHelper / HrCheckBufferSize / HrLockInit / +UnpackLine, etc.). + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow / buffer-size mis-calculation that leads to out-of- +bounds memory access and information disclosure (CWE-190 + CWE-200). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Older code translated a MilPixelFormat value into a bits-per-pixel +(BPP) constant via huge switch/case tables, multiplied the BPP by the +requested pixel count and shifted the product to obtain the needed +byte count: + + bytes = (width * bpp + 7) >> 3; + +The calculation used 32-bit signed intermediates (int / unsigned int). +For large image widths the intermediate product (width * bpp) silently +wrapped above 0xFFFFFFFF, yielding a much smaller value. Subsequent +size checks therefore believed the destination buffer was large enough +and memcpy() / memcpy_0() copied past the end of the caller-supplied +memory (or read past the source). Functions affected include: + + • CBitmap::CopyPixelsHelper() + • CFormatConverter::CopyPixelsHelper() + • HrCheckBufferSize(..) (both overloads) + • CBitmapLock::HrInit() + • CSystemMemoryBitmap::HrLockInternal() + • CLibTiffDecoderBase::UnpackLine() + +The same faulty pattern appeared in several call-sites, so an attacker +could reach it through many public WIC entry points such as +IWICBitmapSource::CopyPixels, IWICBitmapLock, IWICFormatConverter, and +the TIFF decoder. + +Key parameters / structures involved + width/height ............... taken from caller or image + bpp ........................ derived from MilPixelFormat::Enum + stride / cbBufferSize ...... caller-supplied output buffer size + +Because the stride/size mis-match was not detected, WIC leaked process +memory to the attacker (information disclosure) and could also crash. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old (e.g. CopyPixelsHelper) – 32-bit maths overflows +v15 = (a3 * v13 + 7) >> 3; // a3 = pixel count, v13 = bpp +if (a3 > 0x7FFFFFF8 / v13) ... // attempted but insufficient guard +... +memcpy_0(dst, src, v15); // copies past end when v15 wrapped + +// patched – use GetPixelFormatSize + 64-bit and explicit checks +size64 = (uint64)width * GetPixelFormatSize(fmt); +if (size64 > 0xFFFFFFFF || size64 + 7 < size64) error; +needed = (uint32)((size64 + 7) >> 3); +if (needed > callerStride) error; +``` +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Caller (e.g. IWICBitmapSource::CopyPixels) ➜ + CBitmap::CopyPixels ➜ CopyPixelsHelper ➜ + HrCheckBufferSize (wrong arithmetic) ➜ + memcpy() overruns caller buffer ➜ + stale heap / stack bytes returned to attacker. + +Attack Vector +-------------------------------------------------------------------- +Any local program that can feed crafted parameters or image data to +WIC APIs. Typical scenarios: opening a specially crafted TIFF / BMP / +RAW file inside an application that relies on windowscodecs.dll, or +invoking the public ImagingFactory COM interfaces directly. No +special privileges are required – the process calling WIC leaks its +own memory to the attacker. + +Patch Description +-------------------------------------------------------------------- +1. Replaced per-call switch tables with GetPixelFormatSize(), avoiding + manual constants. +2. Performed all size/stride multiplications in 64-bit (unsigned long + long) and compared against 0xFFFFFFFF to detect overflow. +3. Added additional bounds checks (e.g. stride < needed, total + < callerBuffer) before any memcpy. +4. Hardened callers by propagating the new safe helpers and changing + several parameters from signed to unsigned. +5. Updated TIFF unpack path to exercise the central normalisation + table instead of ad-hoc bit manipulations. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could read uninitialised heap / stack +contents belonging to the calling process, potentially exposing +sensitive information such as credentials or ASLR offsets. Depending +on use-case the bug could also lead to out-of-bounds writes and crash. +The issue is therefore an information-disclosure vulnerability in +user-mode (CVE-2025-47980). + +Fix Effectiveness +-------------------------------------------------------------------- +The revised code removes all 32-bit arithmetic, employs central helper +GetPixelFormatSize() and double-checks every calculation for overflow +before memory access. All affected paths were updated uniformly, so +the patch fully mitigates the originally reachable overruns. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47981_negoexts.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47981_negoexts.dll.txt new file mode 100644 index 0000000..53108d9 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47981_negoexts.dll.txt @@ -0,0 +1,112 @@ +{'confidence': 0.37, 'cve': 'CVE-2025-47981', 'patch_store_uid': 'd4ae947a-39d5-4531-8f40-8b422c42f9bb', 'change_count': 4, 'kb': 'KB5062553', 'file': 'negoexts.dll', 'date': 1752036319.733206} +-------------------------------------------------------------------- +CVE-2025-47981 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows authentication stack – negoexts.dll, function +WSTParseMessages(). This routine parses incoming SPNEGO-Extended +Negotiation (NEGOEX) messages received by LSASS and other SSP consumers. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based (and adjacent-stack) buffer overflow caused by an +under-sized destination pointer that is later over-written with network +data via memcpy(). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NEGOEX messages contain two variable-length arrays that are copied out +of the network buffer: + • 12-byte AUTH_SCHEME records – count stored at msg+0x5C + • 16-byte GUID list – count stored at msg+0x54 + +Old logic tried to optimise for small sizes: + 1. Calculate required length L = 12*SchemeCnt or 16*GuidCnt. + 2. If L is below g_ulMaxStackAllocSize it does an alloca(L) to get + stack space; otherwise it calls g_pfnAllocate(L+8) to get heap + space. + 3. Regardless of which branch is taken, it *replaces* the destination + pointer (v31 / v12) with the address of a local 8-byte variable + (&v93 / &v94) before performing the copy: + v31 = &v93; v93 = (__int64*)&v93; + memcpy(v31, attacker_data, L); // L is attacker-controlled + +Because the real buffer is discarded, memcpy() writes up to L-8 bytes +past the tiny pointer variable into adjacent memory. If the allocation +path was taken the overwrite starts inside the freshly allocated heap +chunk header, turning the bug into a bona-fide heap overflow. When the +stack path is taken it clobbers stack frames, producing a classic stack +smash. In both cases the attacker fully controls the over-written +bytes (copied from the network), enabling control-flow hijacking inside +LSASS. + +Key affected fields / parameters + Size check: totalLen (msg[20] / msg[16]) + Count fields: *(WORD*)(msg+0x5C) – auth scheme count + *(WORD*)(msg+0x54) – GUID count + Buffers: v31 / v12 (destination of memcpy) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (before) +v31 = &v93; // points to an 8-byte local on stack +v93 = (__int64 *)&v93; +... +memcpy_0(v31, &v8[offset], 12*iCount); // overflow L bytes + +// fixed (after) +if (use_stack) + buf = alloca(L); +else + buf = g_pfnAllocate(L+8); +... +v22 = buf; // real buffer retained +memcpy_0(v22, &v12[offset], 12*iCount); // copy is now in-bounds +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote attacker sends crafted NEGOEX message with large scheme / GUID + counts and consistent length fields. +2. LSASS calls negoexts!WSTParseMessages() with the attacker buffer. +3. Size sanity checks pass; function enters overflow path. +4. Destination pointer replaced with &v93 / &v94 (8 bytes). +5. memcpy() copies L (attacker-chosen) bytes – corrupting heap or + stack. +6. Subsequent heap free / unlink or return from function allows code + execution under SYSTEM. + +Attack Vector +-------------------------------------------------------------------- +Network. Any protocol that negotiates authentication via SPNEGO/NEGOEX +(SMB, HTTP, RPC, etc.) can deliver the malicious buffer without prior +credentials. + +Patch Description +-------------------------------------------------------------------- +The patched version completely rewrites the temporary-buffer logic: + • Keeps the pointer returned by alloca()/g_pfnAllocate() and never + aliases it with a small local variable. + • Adds VerifyStackAvailable() guard and max-size checks before alloca. + • Falls back to heap allocation if stack is not safe. + • Introduces proper clean-up (WSTDereferencePackage, allocator-aware + frees) on all exit paths. + • Reorganises control flow to remove duplicated risky code. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an unauthenticated attacker could send a single SMB +or HTTP request that causes LSASS to overflow heap metadata or a stack +frame, leading to remote code execution in the Local Security Authority +process (SYSTEM integrity) or, at minimum, process crash (DoS). + +Fix Effectiveness +-------------------------------------------------------------------- +Static diff confirms that the only memcpy() sites now use buffers that +originate from verified alloca() or heap allocations. Pointer +aliasing with small locals is removed, additional length checks were +added, and free paths were hardened. No residual unchecked copies +remain; the overflow condition is eliminated. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47982_storvsp.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47982_storvsp.sys.txt new file mode 100644 index 0000000..c4f04fa --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47982_storvsp.sys.txt @@ -0,0 +1,132 @@ +{'change_count': 2, 'cve': 'CVE-2025-47982', 'file': 'storvsp.sys', 'kb': 'KB5062553', 'confidence': 0.19, 'patch_store_uid': '414cbe0c-e398-454a-bd6e-f517b1fef2ce', 'date': 1752036209.8177946} +-------------------------------------------------------------------- +CVE-2025-47982 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage VSP (storvsp.sys) kernel driver, specifically the +routine InsertEventEntryInLookUpTable that manages internal +"event-entry" bookkeeping structures. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation that results in an index calculation +overflow / out-of-bounds write (CWE-20, CWE-833, CWE-787). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The driver stores performance / telemetry statistics in a per-driver +hash table of EVENT_ENTRY objects. InsertEventEntryInLookUpTable() +receives five parameters: + a1 – context block + a2 – event-id + a3 – provider-id byte + a4 – pointer to caller-supplied statistics block + a5 – **caller-supplied element count** (number of 16-byte fields to + merge) + +Pre-patch logic treated a5 as an *unsigned* byte and used it directly +as the upper bound of the aggregation loop: + do + { + ... use index v12 ... + } + while ((unsigned __int8)v12 < (unsigned int)a5 + 2); + +v12 starts at 2, thus the loop processes (a5+2-2) == a5 iterations. +Because the driver never validates that a5 is less than the number of +16-byte field slots that were actually allocated inside the target +EVENT_ENTRY, an attacker can pass an arbitrarily large value (e.g. +0xFF). The subsequent pointer arithmetic + base = *(QWORD *)(EventEntry+16); + dst = base + 16*index; +walks past the allocated array and the helper _InterlockedAdd64/ +CompareExchange64 routines then **write** to memory outside the object. + +The overwrite occurs while the global shared spin-lock is held, so no +immediate crash is guaranteed; adjacent kernel objects or pointers can +be corrupted, enabling privilege escalation. + +Patch actions show the intent clearly: + • The fifth parameter type was changed from unsigned __int8 to *signed + char* to disallow values >127. + • The aggregation loop was rewritten to bound the index by **internal + metadata (v21)** taken from the existing EVENT_ENTRY instead of by + caller-supplied a5. + • All arithmetic that adds an offset to the base pointer is now + performed only after the new bound check. + +Together these changes eliminate the attacker-controlled upper bound +and therefore the out-of-range memory access. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +LOBYTE(v12) = 2; +if (a5) { + do { + v19 = **(_QWORD **)(a4 + 16 * (unsigned __int8)v12); + v20 = *(_QWORD *)(v16 + 16); + v21 = *(unsigned __int8 *)(v20 + 16 * (unsigned __int8)v12 + 13); + v22 = *(volatile signed __int64 **)(v20 + 16 * (unsigned __int8)v12); + ... write through v22 ... + LOBYTE(v12) = v12 + 1; + } while ((unsigned __int8)v12 < (unsigned int)a5 + 2); +} + +// after +v19 = 2; +if (a5) { + do { + AggregateField(base + 16 * v19, + **(_QWORD **)(a4 + 16 * v19), + *(UINT8 *)(base + 16 * v19 + 13)); + ++v19; + } while (v19 < v21); // v21 == internal field count +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker causes storvsp.sys to call InsertEventEntryInLookUpTable + with a crafted statistics packet. +2. a5 is set to a large value (e.g. 0xFF). +3. Function hashes the entry and locates/creates the destination + EVENT_ENTRY. +4. Aggregation loop iterates a5 times; index > real array size. +5. Pointer arithmetic steps outside EVENT_ENTRY; interlocked writes + corrupt adjacent kernel memory. + +Attack Vector +-------------------------------------------------------------------- +Any local attacker able to invoke the Storage VSP IOCTL path (e.g. +Hyper-V guest or a privileged user-mode component on the host) can +supply a malicious statistics block with an excessive element count. +No special privileges are required beyond the ability to open the +storage VSP device handle. + +Patch Description +-------------------------------------------------------------------- +• Changed parameter type to signed char to halve the representable + range. +• Replaced user-controlled loop bound (a5) with a trustworthy value + extracted from the target EVENT_ENTRY (v21). +• Removed duplicate pointer/size calculations and centralized them in + helper AggregateField(). +• Minor refactoring of lock-handling code; no effect on semantics. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local, authenticated attacker could trigger an +out-of-bounds 8-byte interlocked write in kernel context, leading to +arbitrary kernel memory corruption and therefore Elevation of Privilege +up to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The new bound check uses only internal, validated metadata and the +pointer arithmetic is unchanged; therefore the out-of-bounds condition +is no longer reachable. No residual path keeps the attacker’s value +as an index, making the fix complete. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47984_gdi32full.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47984_gdi32full.dll.txt new file mode 100644 index 0000000..051a88b --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47984_gdi32full.dll.txt @@ -0,0 +1,126 @@ +{'kb': 'KB5062553', 'date': 1752036543.4536552, 'file': 'gdi32full.dll', 'patch_store_uid': 'f41ac86d-0d34-4686-9680-a62744423435', 'cve': 'CVE-2025-47984', 'change_count': 9, 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-47984 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows GDI (gdi32full.dll) – EMF record handler MRSTARTDOC::bPlay, +invoked while playing an EMF+ MR_STARTDOC record (StartDocW setup). + +Vulnerability Class +-------------------------------------------------------------------- +Protection-mechanism failure / insufficient input validation causing +out-of-bounds read (CWE-693 leading to CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +An MR_STARTDOC record stores up to three variable-length WCHAR strings +(lpszDocName, lpszOutput, lpszDatatype) packed back-to-back after the +fixed header. Each string is NUL-terminated and padded to a 4-byte +boundary. The record body therefore contains: + + Offset 0x00 : fixed MR_STARTDOC header (size, flags, etc.) + Offset hdr+0 : DocName[?] + Offset ... : Output[?] + Offset ... : Datatype[?] + +For safe playback MR::bValidOff() must be called with *the exact byte +count from the start of the record* to the end of the referenced field. +The pre-patched code validated each field *individually* but forgot to +add the running offset when DocName is present: + + 1. lengthDoc = StringCbLengthW(DocName) + 2. alignedDoc = (lengthDoc + 6) & ~3 + 3. bValidOff(record, alignedDoc) <-- OK for DocName + 4. ptr = ptr + alignedDoc <-- cursor now outside hdr + 5. lengthOut = StringCbLengthW(Output) + 6. bValidOff(record, (lengthOut + 5) & ~3) <-- BUG – offset resets + +Step 6 merely checked the *size* of Output, not size+offset. An +attacker can craft a record where alignedDoc pushes the cursor beyond +the end of the record and still supply a small lengthOut so that check +passes. The pointer subsequently handed to StartDocW (in user mode) +therefore references memory outside the EMF buffer. The routine reads +that memory to build the DOCINFOW structure and returns it to the +caller or the print spooler, leaking uninitialised heap/stack data. + +Affected variables/structures: + v3 – running pointer to the next string inside the record + v15 – aligned length of DocName + DOCINFOW.lpszDocName / lpszOutput – exported to StartDocW + MR::bValidOff() – boundary check helper (mis-used) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +v11 = (v15 + 6) & 0xFFFFFFFC; // aligned DocName size +if (!MR::bValidOff(this,a3,v11)) // OK + return 0; +v3 += v11; // advance pointer +... +if (!MR::bValidOff(this,a3,(lenOut+5)&~3)) // BUG – offset reset + return 0; +``` +```c +// After +v15 = (v21 + 6) & ~3; +if (FeatureFlag) { + if (lpszOutput && + !MR::bValidOff(this,a3,(int)v15 + (int)v3 - (int)this)) { + RtlLogUnexpectedCodepath(...); + return 0; // new combined-offset check + } +} else if (!MR::bValidOff(this,a3,v15)) { + return 0; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker supplies crafted EMF file. +2. Victim app calls PlayEnhMetaFile / SetEnhMetaFileBits. +3. GDI32 traverses records -> MRSTARTDOC::bPlay. +4. DocName present, Output present. +5. Length of DocName chosen to move cursor past record end. +6. Second bValidOff() passes; pointer now OOB. +7. StartDocW receives out-of-bounds pointers and copies memory, + disclosing process data to attacker (e.g., via print job or network). + +Attack Vector +-------------------------------------------------------------------- +Opening or previewing a malicious EMF file (e-mail attachment, web +content, print-to-file, or remote print job) that is parsed by any +Windows process using GDI32 record playback. + +Patch Description +-------------------------------------------------------------------- +The fix adds a second boundary check that takes the running cursor +position into account. When the optional Output string is present the +code now calls: + + MR::bValidOff(this,a3, alignedDoc + (v3 - this)); + +This validates the *absolute* end offset of DocName inside the record +instead of the relative size alone. Similar logic behind a feature +flag is applied to Output. Additional diagnostic logging via +RtlLogUnexpectedCodepath() was introduced for failed checks. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could read arbitrary process memory +located immediately after the EMF buffer, leaking secrets such as ASLR +bases, heap metadata, or document contents. While no write primitive +exists, the disclosure can be chained with other bugs to defeat modern +mitigations. The issue is therefore classified as an information +leak with network vector (CVSS base 5.5 / 4.3 depending on scoring +model). + +Fix Effectiveness +-------------------------------------------------------------------- +The new bounds check covers the missing condition and is executed every +path where both strings are present. Combined with existing length +checks this closes the out-of-bounds read window. No remaining path +was found where a string pointer can exceed the record size, so the fix +appears complete. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47985_eventtracingmanagement.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47985_eventtracingmanagement.dll.txt new file mode 100644 index 0000000..8079da0 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47985_eventtracingmanagement.dll.txt @@ -0,0 +1,114 @@ +{'confidence': 0.25, 'file': 'eventtracingmanagement.dll', 'kb': 'KB5062553', 'date': 1752037475.682243, 'cve': 'CVE-2025-47985', 'change_count': 2, 'patch_store_uid': '804dd8b4-5422-406c-8d96-73156c78ca46'} +-------------------------------------------------------------------- +CVE-2025-47985 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Event Tracing Management library (eventtracingmanagement.dll). +The affected routines live in the WIL feature-management helper +templates: + • wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestValidate> + • wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestConfNum> + +Vulnerability Class +-------------------------------------------------------------------- +CWE-822: Untrusted Pointer Dereference (local Elevation of Privilege). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the helper +FeatureImpl<...>::__private_IsEnabled() accepted an *arbitrary* pointer +parameter (named a1, type unsigned int*). The routine: + 1. Called GetCachedFeatureEnabledState(a1,&statePtr). + 2. Immediately treated the *return value* of that helper as a second + pointer and performed: + v7 = *(_QWORD *)return_value; // double-deref, unguarded + 3. It also directly read the caller-supplied a1 contents + v2 = *a1; + without validating address, alignment or access rights. + +Because the function executes in the Event Tracing service context +( SYSTEM integrity) any lower-privileged process that can reach +__private_IsEnabled() with a crafted pointer can force the service to +read or write arbitrary kernel / service address space. The read value +(v2 / v7) later influences flags passed to +ReportUsageToService(), enabling controlled corruption of internal +ETW bookkeeping and ultimately privilege escalation. + +The companion routine GetCurrentFeatureEnabledState() relied on +__private_IsEnabled(). Its decision logic used the returned boolean to +adjust a feature-state bitfield stored at *a2, so the untrusted pointer +dereference propagated directly into a shared state structure. + +Parameters / structures involved + • a1 – caller-supplied pointer to a 32-bit feature-state cache word. + • GetCachedFeatureEnabledState() – returns another pointer that was + blindly dereferenced. + • a2 – pointer to caller-visible result structure subsequently + modified with attacker-controlled bits. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v2 = *a1; // 1st unguarded deref +v7 = *(_QWORD *)wil::details::FeatureImpl<...>::GetCachedFeatureEnabledState( + a1, &v8); // double deref of untrusted value +... +wil::details::ReportUsageToService(a1 + 2, 50565209i64, + (v2 >> 10) & 1, (v2 >> 11) & 1, &v5, v3, 0); +``` +```c +// after +wil::details::FeatureImpl<...>::ReportUsage( + (wil::details *)a1, …) // new helper +// no second-level dereference; updates done through +// InterlockedCompareExchange on validated internal cache +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode ETW consumer + -> Feature APIs (wil::Feature<>::IsEnabled/ReportUsage) + -> GetCurrentFeatureEnabledState() + -> __private_IsEnabled(a1) [vulnerable] + -> untrusted pointer dereference + -> corrupted flags + <- back-propagation into *a2 structure + -> ETW service logic executes with modified state + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker supplies a crafted pointer value when +invoking a WIL feature helper reachable from Event Tracing APIs. When +the service dereferences that pointer, the attacker obtains arbitrary +read/write primitives inside the privileged Event Tracing process, +allowing elevation to SYSTEM. + +Patch Description +-------------------------------------------------------------------- +1. __private_IsEnabled() was removed; callers were updated to use a new + ReportUsage() helper that: + • Accepts a *wil::details* object rather than a raw uint32*. + • Performs atomic CompareExchange loops that keep all accesses inside + the process-owned cache object. + • Never dereferences the result of GetCachedFeatureEnabledState(); it + only uses the value returned in an out-parameter. +2. GetCurrentFeatureEnabledState() was rewritten to call the new + ReportUsage() helper and the unsafe logic block was deleted. + +Security Impact +-------------------------------------------------------------------- +Before the fix, a local attacker could coerce the Event Tracing service +into following a user-controlled pointer, enabling arbitrary memory +access and resulting in a full LOCAL SYSTEM privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the untrusted second-level dereference entirely and +confines all memory operations to the internally allocated feature +cache, guarded by Interlocked* primitives. No external pointers are +followed, eliminating the CWE-822 condition. No residual dereference +paths comparable to the removed code are visible, so the fix appears +complete and effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_localspl.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_localspl.dll.txt new file mode 100644 index 0000000..b37b8d7 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_localspl.dll.txt @@ -0,0 +1,110 @@ +{'confidence': 0.12, 'file': 'localspl.dll', 'patch_store_uid': '9319e798-57ab-4eac-b772-de3df940dd67', 'date': 1752037661.1168745, 'cve': 'CVE-2025-47986', 'change_count': 9, 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-47986 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Universal Print / Local Print Spooler – localspl.dll +Function: AssignFreeJobToFreePort() +Build: prior to June-2025 security update (10.0.1904x / 10.0.2539x) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (memory-safety, elevation of privilege) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AssignFreeJobToFreePort() walks the linked list of INIPORT and +INIJOB objects trying to find a printable job ( pointer j ). The loop +runs while the spooler lock is only partially held; another thread can +free the INIJOB structure at any time (e.g. when a user cancels the +job). + +Prior to the patch the routine performed an additional data-type check +without first confirming that the object was still valid: + + v23 = *(const wchar_t **)(j + 120); // read pDatatype + _wcsicmp(L"XPS_PASS", v23); + +If the job had just been deleted, *j now points to freed heap memory +(look-aside list). Reading offset +120 therefore results in a use of +freed memory. By creating and cancelling jobs while heap-spraying, +a local attacker can place controlled data in the freed block and make +the spooler interpret it as a wide-string pointer. Subsequent string +comparison touches the attacker-controlled address space which lies in +a predictable, writable region. Typical exploitation redirects the +flow to attacker-supplied shellcode under the SYSTEM account (the +spooler runs as LocalSystem), providing local privilege escalation. + +The problem is purely temporal: AssignFreeJobToFreePort() caches the +INIJOB pointer ( j ) outside the critical section and later dereferences +it without re-validation. + +Structures / offsets (64-bit): + INIJOB +0x00 FLINK/BLINK + INIJOB +0x20 status flags + INIJOB +0x78 pDatatype (wide string pointer) <-- offset 0x78 = 120 + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (j) { + if (dwFastPrintSlowDownThreshold > *(j+0xA8) && + (!*(DWORD*)(v11+0x228) || + (v23 = *(WCHAR**)(j+0x78)) == NULL || + _wcsicmp(L"XPS_PASS", v23))) { + j = 0; // pointer used after it may be freed + } +} + +// after +if (j && dwFastPrintSlowDownThreshold > *(j+0xA8) && + !*(DWORD*)(v11+0x228)) { + j = 0; // dangerous deref removed +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User submits many print jobs, then quickly cancels them. +2. Spooler thread 1 frees INIJOB record during cancellation. +3. Thread 2 concurrently enters AssignFreeJobToFreePort(), caches ‘j’ + pointer, releases list-lock and later executes extra datatype check. +4. j->pDatatype is read after free – attacker-controlled heap memory. +5. Crafted memory causes controlled crash or code execution inside + spoolsv.exe. + +Attack Vector +-------------------------------------------------------------------- +Local authenticated user (or sandbox escape) who can create / cancel +print jobs interacts with the print spooler IPC (OpenPrinter / +StartDoc / SetJob). No administrator rights are required. + +Patch Description +-------------------------------------------------------------------- +Microsoft removed the unsafe dereference entirely: + • The datatype comparison (_wcsicmp) and the load of *(j+120) were + deleted. + • The pruning logic now only relies on job age threshold and a flag + held in the parent INIPORT ( *(v11+552) ). + • Several variables were renamed; control-flow labels were adjusted + but no new functionality was added. + +No memory layout changes – fix is purely defensive. + +Security Impact +-------------------------------------------------------------------- +Before the update an attacker could elevate privileges from any local +account to SYSTEM by executing code in the context of the spooler +service (Universal Print Management Service). The spooler is enabled +by default on client and server SKUs, making the issue widely +reachable. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated logic completely removes the out-of-lifetime access path, +thereby eliminating the UAF. No residual pointer dereferences of the +freed INIJOB structure remain in this code path. The fix is adequate +as long as no other code dereferences ‘j’ after the lock is released. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_spoolsv.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_spoolsv.exe.txt new file mode 100644 index 0000000..a643041 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_spoolsv.exe.txt @@ -0,0 +1,113 @@ +{'date': 1752037691.7664068, 'patch_store_uid': 'b78f26b8-8fd5-4b88-9d92-123234523668', 'cve': 'CVE-2025-47986', 'kb': 'KB5062553', 'change_count': 41, 'file': 'spoolsv.exe', 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-47986 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Universal Print Management Service (spoolsv.exe) – several +RPC “Async*” entry-points handling printer, driver, port and monitor +management requests. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (resulting in local elevation of privilege). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Every affected RPC stub receives user-controlled Unicode strings +(printer name, driver name, port name, etc.) and immediately builds a +TWorkItem object through a templated CreateInstance<> helper. The +TWorkItem is reference-counted; after the object is queued to the +thread-pool (AddItem) the caller releases its reference, leaving the +object owned solely by the worker crew. + +Before the patch none of the stubs validated the length of the incoming +strings – they were passed straight to CreateInstance(), which in turn +allocates a fixed-size internal buffer (≈0x104 WCHAR) and copies the +content with wcscpy. Supplying an over-long name (>0x104 WCHAR, or for +add-print-processor >0x207 WCHAR) overwrites the TWorkItem’s reference +counter and v-table pointer. When the caller subsequently executes + + NCoreLibrary::TReferenceCount::Release( WorkItem ); + +on the already corrupted object, the manipulated ref-count reaches +zero and the object is freed, while a pointer to it is still en-queued +inside the worker thread list. Any later access by the worker crew +operates on freed memory – a classic use-after-free that allows the +attacker to place a forged object/v-table and obtain arbitrary code +execution in the SYSTEM-level spoolsv.exe process. + +Functions changed by the patch include (non-exhaustive): + • RpcAsyncEnumPrinters + • RpcAsyncEnumPrinterDrivers / _Datatypes / _Processors + • RpcAsyncGet/SetPrinterData(Ex) + • RpcAsyncAdd/Delete* (Monitor, Driver, Port, Form, …) +Each of them shared the same vulnerable pattern. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before – RpcAsyncEnumPrintProcessors +Reply = 0; +Instance = CreateInstance(&YEnumPrintProcessors, ...); +if (Instance < 0 || + (Instance = WorkCrew::AddItem(g_pWorkCrew, Reply), + Release(Reply), Instance < 0)) +{ + Reply = (USHORT)Instance; // use-after-free window + RpcAsyncCompleteCall(pAsync,&Reply); +} + +// After – first lines of every stub +if (FeatureEnabled && (a3 && wcsnlen(a3,0x104)>=0x104 || + a4 && wcsnlen(a4,0x104)>=0x104)) +{ + LOWORD(a4)=87; // ERROR_INVALID_PARAMETER + RpcAsyncCompleteCall(...); + return; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged attacker opens the named pipe "\\.\pipe\spoolss". +2. Sends an RPC call to any of the vulnerable *Async* procedures with a + very long printer/driver/port string. +3. spoolsv.exe allocates TWorkItem, overflows the fixed buffer and + corrupts the object’s ref-count. +4. Caller releases the object – memory is freed. +5. Worker thread later dereferences the stale pointer, executing attacker + data under SYSTEM integrity. + +Attack Vector +-------------------------------------------------------------------- +Local – requires the ability to talk to the spooler RPC interface. Any +normal user on the machine can reach the interface, therefore the bug +enables a straightforward privilege escalation to SYSTEM. + +Patch Description +-------------------------------------------------------------------- +• Added strict length validation: every RPC stub now calls wcsnlen() and + rejects strings longer than 0x104 (260) WCHARs, or 0x207 for the file + name parameter in AddPrintProcessor. +• Returns ERROR_INVALID_PARAMETER (87) and completes the async call + early when validation fails – no object allocation occurs. +• Added “Feature 2578215227” gate so that checks can be switched on/off + through Windows feature flags. +• No structural change of TWorkItem – mitigation is purely input + sanitisation to keep buffer copies within bounds. + +Security Impact +-------------------------------------------------------------------- +Before the fix a normal domain / local user could reliably corrupt heap +structures inside spoolsv.exe and run arbitrary code with SYSTEM +privileges – full local elevation. No user interaction is required and +exploitation is entirely local. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched stubs exit before any heap allocation when supplied with an +over-long string, fully preventing the overwrite that led to the use- +after-free. No other code paths to CreateInstance() are reachable with +unchecked input, so the vulnerability is effectively closed. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_spoolsvworker.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_spoolsvworker.exe.txt new file mode 100644 index 0000000..45217b0 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47986_spoolsvworker.exe.txt @@ -0,0 +1,105 @@ +{'confidence': 0.24, 'patch_store_uid': '90c79bb9-c311-42d0-967c-20ed5bb84c1d', 'date': 1752037744.894354, 'file': 'spoolsvworker.exe', 'change_count': 40, 'cve': 'CVE-2025-47986', 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-47986 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Printing Sub-system – spoolsvworker.exe (Universal Print +Management Service) RPC async handlers and helper routines. + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free (CWE-416) triggered by uncontrolled string length / +missing parameter validation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Many async RPC entry points (e.g. RpcAsyncEnumPrinters, EnumPorts, +AddMonitor, DeletePrinterKey, DeletePrinterDataEx, AddPrinter, etc.) +create a heap object derived from NThreadingLibrary::TWorkItem via +TFunction<N>::CreateInstance(). + +The original code blindly forwards caller-supplied UTF-16 strings +(printer name, port name, driver name, form name, monitor name, etc.) +into that CreateInstance() helper. Internally these names are copied +into fixed-size stack/heap buffers (typically 0x104 or 0x400 WCHARs). +When a caller specifies an over-length string, the copy overruns the +allocation, corrupts the reference-count field that lives just before +/ after the buffer, and subsequently causes the work-item object to be +prematurely released while still queued – classic use-after-free. A +local low-privilege client can then reclaim the freed chunk with +controlled data and execute code in the privileged +spoolsvworker.exe (runs as LocalSystem). + +Affected structures / variables +• NThreadingLibrary::TWorkItem – ref-count at offset +8 +• TFunction<N>::CreateInstance() – copies strings into embedded + WCHAR[0x104]/[0x400] buffer without bounds checking +• RpcAsync* wrappers – no wcslen/wcsnlen verification + +Once the corrupted WorkItem is dequeued the worker thread calls the +virtual destructor through the overwritten vftable pointer, leading to +arbitrary code execution in kernel printing context and privilege +escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (RpcAsyncEnumPrinters) +TFunction8<...>::CreateInstance(v12, a3, ... , a4); +// no length check on a3 / a4 (printer & server name) + +// AFTER +if ( wil::details::FeatureImpl<...>::__private_IsEnabled(...) + && a4 && wcsnlen(a4, 0x104) >= 0x104 ) { + LOWORD(a3)=ERROR_INVALID_PARAMETER; /*87*/ + RpcAsyncCompleteCall(...); + return; /* bail out safely */ +} +``` +(Almost every RpcAsync* entry received the same pattern – verify string +length against 0x104 or 0x400 and fail early.) + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client (local user) → RPC endpoint "winspool" → any *RpcAsync*Xxx +procedure → overly long WCHAR string passed → TFunction<N>::CreateInstance +copies into small buffer → ref-count / vtable memory smashed → work item +freed while still referenced → later dereference in worker thread → UAF +and arbitrary code execution. + +Attack Vector +-------------------------------------------------------------------- +Local authenticated user, or sandboxed process able to call the +Spooler RPC interface, sends an async RPC request with an over-sized +printer / port / driver / form / monitor string (>0x104 or >0x400 +WCHARs depending on call) to spoolsvworker.exe. + +Patch Description +-------------------------------------------------------------------- +1. Input sanitisation: + • All vulnerable RpcAsync* entry points now validate each incoming + UTF-16 string with wcsnlen(…, MAX) where MAX is 0x104 (260) or + 0x400 (1024) chars. Oversized inputs immediately return + ERROR_INVALID_PARAMETER (87) and no work-item is created. +2. Feature flag gating (Wil feature 1286131003): length checks are only + active when the feature is enabled – the update turns it on. +3. Hardened helper routines (DeleteSymbolicLink(), FeatureImpl:: + GetCurrentFeatureEnabledState(), etc.) but the critical fix is the + uniform parameter length validation. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could reliably corrupt heap control +structures inside a privileged service, gaining SYSTEM privileges. +Because the service is always running, the issue enables a full local +Elevation of Privilege (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The added wcsnlen() checks guarantee that no string longer than the +internal buffer is accepted, preventing the buffer overrun and the +subsequent ref-count corruption/UAF. No further code paths were found +that bypass these guards; therefore the patch is considered effective +for the described vulnerability. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47987_tspkg.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47987_tspkg.dll.txt new file mode 100644 index 0000000..a541c86 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47987_tspkg.dll.txt @@ -0,0 +1,116 @@ +{'date': 1752036349.705348, 'cve': 'CVE-2025-47987', 'change_count': 1, 'patch_store_uid': '78242d86-7627-4023-a043-fd9bdcd7d882', 'confidence': 0.33, 'kb': 'KB5062553', 'file': 'tspkg.dll'} +-------------------------------------------------------------------- +CVE-2025-47987 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Terminal Services Package (tspkg.dll) – routine +TSCreateKerbCertLogonBuffer(), used by the CredSSP protocol to build +KDC_CERT_LOGON request buffers for smart-card / certificate based +Kerberos logon. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based Buffer Overflow +CWE-190: Integer Overflow or Wraparound (precursor) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper TSCreateKerbCertLogonBuffer() receives three UNICODE_STRING +structures (user, domain, workstation), an arbitrary data blob (a4) and +its length (Size). It must allocate a single contiguous heap buffer +that holds an internal header (93 bytes), the three strings (each padded +to an 8-byte boundary) and finally the arbitrary blob. + +Pre-patch logic: + tmp = a2->Length + a1->Length + a3->Length + 93; + allocSize = Size + (tmp & 0xFFFFFFF8); // v11 – 32-bit unsigned +The result is stored in a 32-bit variable and handed to TSAllocate(). +If Size is chosen so that the 32-bit addition wraps (e.g. tmp=0xFFFFFFF8, +Size=0x20), allocSize becomes 0x10, TSAllocate() returns a 16-byte +buffer, yet the routine continues to copy tmp+Size ( >4 GB ) bytes into +it through several memcpy_0 calls. The overflow overwrites the heap’s +metadata and adjacent allocations under the caller’s security context. + +Because CredSSP runs inside the LSA process (lsass.exe) on the server +side and inside mstsc.exe / WinLogon on the client side, the overwrite +can lead to code execution in High / SYSTEM integrity, yielding local +privilege escalation. + +The vulnerable arithmetic involves the following parameters and layout: + Header : 0x5D bytes (rounded up to 0x60) + UserLen : a1->Length + DomainLen : a2->Length + WorkstationLen : a3->Length + DataBlobLen : Size (caller-controlled) + allocSize (v11) : 32-bit sum that may wrap + OutBuffer (v13) : heap block returned by TSAllocate() + +No bound checks exist after allocation; all offsets are derived from the +original 16-bit string lengths and the untruncated Size, so the overflow +occurs deterministically when the wraparound is triggered. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch +v11 = Size + ((a2->Length + a1->Length + a3->Length + 93) & 0xFFFFFFF8); +... +memcpy_0(v20, a4, (unsigned int)Size); // writes Size bytes beyond v11 +``` +```c +// after patch (excerpt) +v11 = (a2->Length + a1->Length + a3->Length + 93) & 0xFFFFFFF8; +v12 = v11 + Size; +if (FeatureEnabled && v12 < v11) // detect wrap + return STATUS_INTEGER_OVERFLOW; // 0xC0000095 (mapped) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker-controlled client code calls CredSSP API that ultimately + invokes TSCreateKerbCertLogonBuffer(), supplying: + a1/a2/a3 : benign strings + Size : 0xFFFF'FFF0 + delta (causes wrap) +2. Integer addition allocSize=Size+tmp wraps to small positive value. +3. TSAllocate() returns undersized heap buffer. +4. Subsequent memcpy_0() copies full, non-wrapped Size bytes, smashing + the heap. +5. Corrupted heap structures lead to controlled pointer overwrite and + code execution inside the hosting process (e.g., lsass.exe). + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker capable of invoking CredSSP routines +(e.g., via Remote Desktop client plug-ins or by spawning a process under +his account that requests a certificate-based logon) supplies crafted +length fields to trigger the overflow and run arbitrary code with SYSTEM +privileges. + +Patch Description +-------------------------------------------------------------------- +The patch splits the size computation: + alignedHeader = (sumLen + 93) & 0xFFFFFFF8; + totalSize = alignedHeader + Size; +It then checks for unsigned 32-bit wraparound: + if (FeatureEnabled && totalSize < alignedHeader) + return STATUS_INTEGER_OVERFLOW; +If the check passes, TSAllocate() is called with the *unwrapped* size +(totalSize). No other functional changes were introduced. + +Security Impact +-------------------------------------------------------------------- +Before the patch, a malicious user could reliably overflow a heap buffer +inside a high-privilege process, leading to local elevation of privilege +(SYSTEM) and possible credential theft in the LSA context. Remote code +execution on an RDP server is also plausible if the attacker already has +valid credentials. + +Fix Effectiveness +-------------------------------------------------------------------- +The wraparound test ensures that any addition that loses the upper +32-bit carries is detected and aborted, preventing an undersized +allocation. Because all subsequent copies depend on the same length +variables, the overflow cannot occur once the allocation size is +correct. Therefore the patch fully mitigates the identified flaw. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47991_imebroker.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47991_imebroker.exe.txt new file mode 100644 index 0000000..2a1f423 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-47991_imebroker.exe.txt @@ -0,0 +1,128 @@ +{'date': 1752036443.9542558, 'kb': 'KB5062553', 'change_count': 2, 'cve': 'CVE-2025-47991', 'file': 'imebroker.exe', 'patch_store_uid': 'd9a8762c-5eb8-49b2-b991-6bb9d51049c6', 'confidence': 0.24} +-------------------------------------------------------------------- +CVE-2025-47991 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +imebroker.exe – Windows Input Method Editor (IME) feature-flag helper +code inside wil::details::FeatureImpl<*> that is linked into the +broker service running under elevated privileges. + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each FeatureImpl instance keeps a small, dynamically allocated cache +object that mirrors the current Feature Control (FC) configuration. +The first double-word of this cache is treated as a bit field: + bit0 – cache initialised (0x1) + bit1 – current flag value valid (0x2) + bit2 – cache is subscribed to FC (0x4) +If the object is not subscribed (bit2==0) the FC layer can free and +re-allocate it at any time when the configuration changes. + +In the original helper + wil::details::FeatureImpl<...>::__private_IsEnabled() +only bits0/1 were checked. When bit2 was clear the function still +trusted the pointer returned by GetCachedFeatureEnabledState(), stored +it inside *a1, and immediately dereferenced it to obtain the 64-bit +state value: + + v7 = *(_QWORD *)GetCachedFeatureEnabledState(a1, &v8); + v2 = v7; // use the contents + +Because the object was **not** subscribed it could be freed by another +thread between the call and the dereference, leaving *a1 pointing to +released memory. Any later read or write to the cache produced a use +after free inside the high-privilege IME broker process. + +The same stale pointer path was reachable from +Feature_TestConfNum::GetCurrentFeatureEnabledState() which, in the +old build, called the vulnerable __private_IsEnabled() helper while +holding only its own stack variables. An attacker able to cause +rapid FC state churn (e.g. through the public Feature Management +API) could win the race and force the broker to dereference freed +memory, leading to arbitrary code execution in the broker’s +context. + +Parameters/structures involved + a1 – pointer to the per-feature cache header (volatile ULONG) + GetCachedFeatureEnabledState() – supplies, but does not pin, the + dynamic cache block that may be freed asynchronously. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – before patch +v7 = *(_QWORD *)wil::details::FeatureImpl<...>::GetCachedFeatureEnabledState( + a1, + &v8); // &v8 is only 1 byte – wrong size is passed +v2 = v7; // dereference possible freed block +... +if ( (*a1 & 4) == 0 ) // not subscribed + ...same pattern again... +``` +```c +// fixed – after patch +if ((*(_DWORD *)a1 & 4) == 0) { + v8 = wil::details::EnsureSubscribedToFeatureConfigurationChanges(a1); + ... + // atomically set bits 1 and 2 + v12 = _InterlockedCompareExchange((volatile signed __int32 *)a1, + v11, + v6); + ... + wil::details::SubscribeFeatureStateCacheToConfigurationChanges(a1, + 3, v9); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker toggles a WIL feature through the public API, forcing the + FC service to free and re-allocate the per-feature cache. +2. A second thread in IMEbroker calls + Feature_TestConfNum::GetCurrentFeatureEnabledState() +3. That routine calls the old + Feature_TestValidate::__private_IsEnabled() +4. __private_IsEnabled() reads *a1 while bit2==0 and immediately + dereferences the freed cache object – UAF. + +Attack Vector +-------------------------------------------------------------------- +Any local user that can call the Windows Feature Management APIs or +otherwise trigger rapid configuration changes can race the broker and +hit the dangling pointer. No special privileges are needed to start +the race; exploitation yields code execution in the elevated +imebroker.exe process, enabling privilege escalation. + +Patch Description +-------------------------------------------------------------------- +Microsoft removed the unsafe __private_IsEnabled() helper and replaced +it with a new ReportUsage() routine that: + • Calls EnsureSubscribedToFeatureConfigurationChanges() when bit2 is + clear and, if needed, subscribes the cache by calling + SubscribeFeatureStateCacheToConfigurationChanges(). + • Uses InterlockedCompareExchange to update the flag word atomically + so that the cache is either fully initialised+subscribed or not + used at all. + • Never dereferences the cache pointer until subscription is in + place, eliminating the window where the memory could be freed. + +Security Impact +-------------------------------------------------------------------- +Dereferencing a freed FC cache object allows the attacker to corrupt +process memory and execute arbitrary code inside imebroker.exe, +resulting in a local elevation of privilege (EoP) to the broker’s +integrity level (typically SYSTEM on desktop Windows). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch adds the missing lifetime management (subscription and +atomic flag update), so the cache pointer can no longer become stale +between acquisition and use. All direct calls to the old vulnerable +helper were removed. Barring additional undiscovered cache paths the +UAF is fully mitigated. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48001_fvevol.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48001_fvevol.sys.txt new file mode 100644 index 0000000..8c8d4d0 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48001_fvevol.sys.txt @@ -0,0 +1,118 @@ +{'cve': 'CVE-2025-48001', 'change_count': 42, 'date': 1752036361.500981, 'file': 'fvevol.sys', 'confidence': 0.19, 'patch_store_uid': '546cc225-5ff3-4cd7-be6d-d4d62c9a8c45', 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-48001 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft BitLocker full-volume encryption driver (fvevol.sys) – +ICE (Inline Crypto Engine) key-management and hardware-wrap support +routines. + +Vulnerability Class +-------------------------------------------------------------------- +Time-of-check / time-of-use (TOCTOU) race condition combined with +inadequate parameter validation that allows a security-feature +bypass. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper routines that process hardware-wrapped FVEK material +trusted size and capability values that were read **before** the +actual key-wrap/unwrap operations were executed. The original code +assumed: + • the caller-supplied WRAP_KEY_REQUEST structure had already been + validated (FveIsValidWrapKeyRequest) + • driver-global ICE-capability data were static and could be used + without re-checking (SimIceKmiWrapKey / SimIceKmiInsertChallenge) + • the requested key-blob length could be represented as + KeySize+24 without risk of integer wrap (former + FveIceKeyToFveKeyInfo) + +Attackers could exploit those assumptions in a physical or +pre-boot/DMA scenario: + 1. Issue IOCTLs that deliver a small (≤0x14-byte) request buffer. + 2. Race the device so that, between the driver’s initial size check + and its later use, a much larger buffer is DMA-written. Because + the driver never re-validated the buffer and had already + allocated a pool chunk based on the old size, the new data + overwrite adjacent kernel memory. + 3. By carefully crafting the overrun, the attacker can force the + driver to accept a forged FVEK as ‘hardware wrapped’, causing + BitLocker to mount the volume without the original TPM/USB/ PIN + unlock material. + +Functions involved (before patch) + • FveIceKeyToFveKeyInfo() – no integer-overflow guard when adding + 24 bytes to user length + • SimIceKmiWrapKey() / SimIceKmiInsertChallenge() – no capability + enquiry; only constant 0x20/0x40 size checks + • FveIsValidWrapKeyRequest() – accepted any request ≤0x20 bytes; + never compared against device capabilities. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch – SimIceKmiWrapKey +if (!a2 || !a3 || !a4 || *a3 > 0x20u) // ignores hw limits + return STATUS_INVALID_PARAMETER; +... +* a4 >= 0x118u && a5 // accepts huge copy +memmove(a5 + 88, a2 + 24, *(USHORT*)(a2+22)); +``` +```c +// fixed – FveIceKmiValidateUnwrapKey (new) +if ((int)KeyLen + 24 < KeyLen) // overflow guard + return STATUS_INTEGER_OVERFLOW; +... +Kmi = SimIceKmiQueryCapabilities(dev,&Caps); +if ((Caps.Flags & 1) && KeyLen > Caps.Max) // re-check caps + return STATUS_INVALID_PARAMETER; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +IOCTL_FVE_HW_WRAP_FVEK → IoctlFveHardwareWrapFvek() + → FveIsValidWrapKeyRequest() (size only) + → SimIceKmiWrapKey()/InsertChallenge() (alloc based on stale size) + → overwrite pool → corrupted ICE key structure → BitLocker mounted + with attacker-supplied key. + +Attack Vector +-------------------------------------------------------------------- +An attacker with physical access (pre-boot console, DMA, or malicious +storage controller firmware) supplies a malicious +WRAP_KEY/CHALLENGE_KEY buffer whose size is changed after the initial +check but before the driver copies the data. The corrupted in-kernel +ICE key bypasses normal BitLocker unlock requirements. + +Patch Description +-------------------------------------------------------------------- +1. Introduced FveIceKmiValidateUnwrapKey() – performs atomic + capability query, integer-overflow detection, and single, trusted + copy of user data. +2. Re-wrote FveIsValidWrapKeyRequest(), SimIceKmiWrapKey() and + SimIceKmiInsertChallenge() to: + • call SimIceKmiQueryCapabilities() and honour per-device limits + • bail out on any inconsistency between reported and actual sizes + • zeroise and free on all failure paths. +3. Added ExAllocatePool2()/RtlZeroMemory hardened allocations and + explicit integer-addition overflow checks ((len+24) < len). +4. Added extensive WPP tracing for failure paths. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could substitute or forge a wrapped +FVEK and convince BitLocker to use it, effectively decrypting the +volume without the original protectors. This bypasses the encryption +feature and may expose all plaintext data (Security Feature Bypass; +compromises confidentiality & integrity). + +Fix Effectiveness +-------------------------------------------------------------------- +The new code eliminates the TOCTOU window by validating size and +capabilities **after** copying into driver-owned memory and before +use, adds integer-overflow checks, and rejects any request that +exceeds the device-reported maximum. All affected paths now zero and +free sensitive buffers on error, preventing both the race and memory +corruption. No bypass has been found with the patched logic. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48002_vmwp.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48002_vmwp.exe.txt new file mode 100644 index 0000000..c290470 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48002_vmwp.exe.txt @@ -0,0 +1,117 @@ +{'kb': 'KB5062553', 'date': 1752037795.819313, 'patch_store_uid': '8aafed3b-809e-47fe-b58d-87ad406ad8af', 'change_count': 4, 'file': 'vmwp.exe', 'confidence': 0.26, 'cve': 'CVE-2025-48002'} +-------------------------------------------------------------------- +CVE-2025-48002 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V user-mode worker process (vmwp.exe). The affected +routines belong to the WIL feature-gating helper templates generated +inside vmwp.exe: + * Feature_TestConfNum::GetCurrentFeatureEnabledState + * Feature_ImplVal::GetCurrentFeatureEnabledState + * Feature_TestValidate::ReportUsage + +Vulnerability Class +-------------------------------------------------------------------- +CWE-190: Integer overflow / wraparound leading to CWE-125: out-of-bounds +read (information disclosure). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +On x64 Windows the first four integer/pointer parameters are passed in +registers RCX, RDX, R8, R9. Prior to the fix the template instance +wil::details::FeatureImpl<...>::ReportUsage was emitted with the +following prototype: + ReportUsage(unsigned int *impl, unsigned __int8 reportingKind) + +However *every* call-site supplied only the first argument, because the +intended design is that the helper figures out the reporting kind by +itself. When the callee executed it still tried to consume the second +parameter, blindly interpreting whatever happened to be in the DL +register (high byte of RCX or residual stack data) as an 8-bit +reporting kind. + +That byte is subsequently propagated to +wil::details::ReportUsageToService, which copies the value into a +telemetry / diagnostic buffer that is returned to the guest or to an +adjacent management component. Consequently arbitrary stack or register +bytes from the Hyper-V worker process leak outside the VM boundary. + +In addition, the helper stored the 32-bit feature mask in 8-bit +variables (char v9 / v11). Any value >=0x100 silently wrapped, masking +upper bits (CWE-190). The wrapped value is later used in bit tests and +conditionals that gate whether 0x400 (FEATURE_PRIVACY_GUARDED) is +cleared. By forcing the overflow an attacker can bypass that guard and +make ReportUsage expose data that should have been suppressed. + +The combined effects give the guest (or another adjacent tenant able to +influence feature activation) a reliable primitive to disclose vmwp.exe +stack memory. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable prototype (before) +__int64 __fastcall ReportUsage(unsigned int *impl, + unsigned __int8 reportingKind); + +// fixed prototype (after) +__int64 __fastcall ReportUsage(unsigned int *impl); +``` +```c +// truncated flag handling (before) +char v9; // CL -> receives 32-bit OR result, wraps at 0xFF +... +if (v11 && !v9) + *a2 &= ~0x400u; // guard can be skipped +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Guest / management input that toggles a feature flag + --> vmwp!GetCurrentFeatureEnabledState + (builds the 32-bit mask) + --> if mask meets (0xC00==0xC00 || mask&0x40) path + vmwp!Feature_TestValidate::ReportUsage [bad call] + |__ consumes stray DL byte (integer wrap) + |__ wil::ReportUsageToService serialises data + --> Data copied back to guest / network => leak + +Attack Vector +-------------------------------------------------------------------- +An authenticated attacker running in an adjacent guest or management +context issues configuration changes or hypercalls that cause the host +worker process to evaluate the affected feature flags. The malformed +call to ReportUsage then exfiltrates host stack bytes via the normal +usage-report path. + +Patch Description +-------------------------------------------------------------------- +1. ReportUsage signature reduced to a single parameter; all call-sites + updated accordingly. +2. GetCurrentFeatureEnabledState paths switched from __private_IsEnabled + to the corrected ReportUsage helper, and the compound condition was + simplified ("||" instead of separate branches). +3. Boolean work variables upgraded from char to int, eliminating 8-bit + truncation. +4. Wrong trait id passed to GetCachedFeatureEnabledState replaced with + the correct one. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could read up to one byte of uninitialised +vmwp.exe stack per invocation of ReportUsage. Repeated calls allow the +collection of larger amounts of memory, potentially including host +ASLR/CFG material, guest secrets from neighbouring VMs, or Hyper-V +internal pointers. The vulnerability does not grant write +capabilities, but the disclosed information is sufficient for +subsequent exploitation stages. + +Fix Effectiveness +-------------------------------------------------------------------- +The parameter-count mismatch is removed and no residual paths call the +old two-parameter stub; integer truncation has been eliminated. +Therefore the identified disclosure primitive is fully neutralised. +No alternative path performing the same out-of-bounds read was +observed in the patched binary, so the fix is considered effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48003_fvevol.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48003_fvevol.sys.txt new file mode 100644 index 0000000..5e19a76 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48003_fvevol.sys.txt @@ -0,0 +1,125 @@ +{'file': 'fvevol.sys', 'change_count': 42, 'date': 1752036564.2785306, 'kb': 'KB5062553', 'confidence': 0.21, 'cve': 'CVE-2025-48003', 'patch_store_uid': '546cc225-5ff3-4cd7-be6d-d4d62c9a8c45'} +-------------------------------------------------------------------- +CVE-2025-48003 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft BitLocker driver (fvevol.sys) – Simulated Integrated +Cipher-Engine (ICE) Key-Management Interface (KMI) helper routines +SimIceKmiWrapKey() / SimIceKmiInsertChallenge() and related clean-up +code paths. + +Vulnerability Class +-------------------------------------------------------------------- +Protection-mechanism failure / improper input validation (CWE-693). +The driver assumed that the underlying ICE/KMI device always +implemented particular capabilities and therefore accepted and stored +incomplete or out-of-spec key-handling data, allowing BitLocker +protection to be bypassed by physical attack. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Two helper routines are reachable through BitLocker’s user / firmware +communication channel: + + • SimIceKmiWrapKey() – builds a 0x118-byte ICE_WRAPPED_KEY block. + • SimIceKmiInsertChallenge() – copies an arbitrary length challenge + into the same structure at offset +0x98. + +Before the patch both functions verified their parameters only against +hard-coded constants: + – key-length <= 0x20 bytes + – challenge <= 0x20 bytes + – wrapped key <= 0x40 bytes +They never asked the real ICE/KMI device whether those limits were +actually correct, nor did they check if the device supported the +requested operation at all. + +Consequences of the missing checks +---------------------------------- +1. A malicious actor with physical access can present a storage device + that **reports no ICE/KMI capability** yet still supplies superficially + valid parameter buffers. +2. fvevol accepts the buffers, fills in the per-volume ICE_KEY + structure located at devExt+0x11C8 (referenced at a1+4552) and + marks the key as present. +3. Once the in-memory flag is set, subsequent paths such as + FveFilterSurpriseRemoveDevice() and FveDeleteIceKey() skip critical + provenance checks (the code assumes the firmware has already + authenticated the volume-master-key). +4. During early boot or from an offline environment an attacker can + swap or tamper with the disk and still pass BitLocker’s device + usage policy, effectively bypassing the encryption safeguard. + +Because the driver relied on a *logical* protection mechanism (device +capability enforcement) that could be forged, BitLocker’s physical +protection guarantee could be defeated without touching the cryptography +itself. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – SimIceKmiWrapKey() +if (!a2 || !a3 || !a4 || *a3 > 0x20u || + *(_DWORD *)(a2+16) || *WORD(a2+22) > 0x40u || + (*BYTE)(a2+20) & 1) + return STATUS_LOGON_FAILURE; +... +memmove(a5+88, (void *)(a2+24), *(WORD *)(a2+22)); + +// before patch – SimIceKmiInsertChallenge() +if (!a2 || !a3 || !a4 || *a3 < 0x118u || *a2 > 0x20u) + return STATUS_LOGON_FAILURE; +memmove((void *)(a4+152), a2+1, *a2); +``` +The absence of SimIceKmiQueryCapabilities() means size and feature +information were never corroborated. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker connects or emulates a storage device during boot. +2. Windows mounts the volume; fvevol issues ICE/KMI IOCTLs. +3. Crafted responses reach SimIceKmiWrapKey() / InsertChallenge(). +4. Functions accept data, set devExt->IceKeyPresent flag. +5. Volume is considered protected although no valid key exchange ever + happened, enabling access to the encrypted data. + +Attack Vector +-------------------------------------------------------------------- +Physical access – hot-plug or preload a malicious storage controller / +firmware that forges ICE/KMI capability responses, or use a custom PE +environment to send crafted IOCTLs to fvevol before Windows boots the +protected OS volume. + +Patch Description +-------------------------------------------------------------------- +• Both routines now invoke SimIceKmiQueryCapabilities() to retrieve a + 128-bit capability record. +• Validation is performed against capability-specific maxima instead of + fixed magic numbers. +• Operations are refused if the device advertises no support, if + required auxiliary buffers are missing, or if lengths exceed what the + device claims to handle. +• Consistent STATUS_INVALID_PARAMETER / STATUS_NOT_SUPPORTED error codes + replace previously misleading ones. +• Additional defence-in-depth: lock acquisition, read/write rundown and + extra zeroisation paths were added to FveFilterSurpriseRemoveDevice() + and FveDeleteIceKey(). + +Security Impact +-------------------------------------------------------------------- +Without the patch, BitLocker may erroneously mark a volume key as +validated, allowing an attacker with physical access to circumvent the +boot-time protection and unlock or manipulate encrypted data. The issue +does not require code execution and therefore is classified as a +Security Feature Bypass rather than privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The added capability query and parameter checks close the direct logic +flaw. Provided the capability data returned by hardware / firmware is +trustworthy, the bypass is neutralised. No residual path accepting +unverified key material was observed in the patched code; however the +overall assurance still depends on integrity of the underlying +ICE/KMI implementation, which is outside the scope of this fix. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48799_servicinguapi.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48799_servicinguapi.dll.txt new file mode 100644 index 0000000..44cda6a --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48799_servicinguapi.dll.txt @@ -0,0 +1,119 @@ +{'change_count': 22, 'patch_store_uid': 'ab70aca1-b754-48e9-a911-6baa7a69e726', 'cve': 'CVE-2025-48799', 'file': 'servicinguapi.dll', 'confidence': 0.04, 'kb': 'KB5062553', 'date': 1752036927.3165164} +-------------------------------------------------------------------- +CVE-2025-48799 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Update Servicing Stack (servicinguapi.dll) – specifically the +Arbiter code that builds update “action lists” via +CreateActionListInternalExclusive() and the feature-flag helper +GetCachedFeatureEnabledState(). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-59: Improper Link Resolution Before File Access ("Link Following") + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CreateActionListInternalExclusive() is meant to guarantee that only one +writer at a time can build an action list by taking a named mutex +("BDA50A95-17AC-40FE-83B6-DAE899448200"). In the pre-patch version the +function contained a short-circuit: + • If either Cbscore.dll or ServicingUAPI.dll was already loaded the + code concluded that it had been invoked from the servicing stack + itself and executed the update logic **without ever taking the + mutex** (see "skip locking" message in the old code). + +Because GetModuleHandleExW only checks the module table, an unprivileged +process running inside the Windows Update service could pre-load one of +those DLL names (Built-in DLL search order or side-load). The service +would then enter the mutex-free path while still running with SYSTEM +rights. + +Once inside this path CreateActionListInternal() performs extensive file +operations under %windir%\Servicing. No validation is performed to +verify that every destination resides on an ordinary directory entry. +An attacker can therefore plant a hard-link or directory junction that +redirects one of those writes to an arbitrary file. When the service +later opens the file with SYSTEM privileges it follows the link (CWE-59) +and overwrites the attacker-chosen target, yielding an elevation of +privilege. + +Secondary issues fixed at the same time: + • If CreateMutexW failed the handle variable remained NULL but + WaitForSingleObject was still called, creating undefined behaviour. + • Several early-error paths leaked the mutex handle, allowing a + subsequent call to believe the lock had been taken. + • GetCachedFeatureEnabledState() contained complex, error-prone bit + twiddling when caching the feature flag. While not directly + exploitable, it drove the faulty logic that decided whether the + mutex could be skipped. + +Patch Description +-------------------------------------------------------------------- +1. A new feature gate (Feature_Servicing_ARBOffGlobal) was introduced. + Only when this flag is **explicitly enabled** will the code bypass + locking; the previous implicit test on the two module names is + removed. +2. All mutex handling was rewritten around AutoMutexLocker: + – the mutex handle is always validated, + – acquisition failures are converted to HRESULTs and returned, + – every exit path releases and closes the handle. +3. GetCachedFeatureEnabledState() was reduced to a minimal and safe + implementation that simply queries the current flag and writes the + cache atomically. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Pre-patch : lock is skipped purely on module presence +if (ModuleHandle || v16) { + LogAdapter::TraceAtLevel(...,"skip locking"); + goto LABEL_36; // runs with no mutex +} +... +MutexW = CreateMutexW(...); +if (!MutexW) // NULL handle can reach WaitForSingleObject + ... // undefined behaviour +``` +```c +// Post-patch : lock bypass gated by explicit feature flag +if (FeatureImpl<...ARBOffGlobal>::__private_IsEnabled(...)) { + // Offline/servicing-stack paths still allowed, but *only* when + // the flag is set through flighting policy. + LogAdapter::TraceAtLevel(...); +} else { + Handle = CreateMutexW(...); + AutoMutexLocker::Lock(&Handle, timeout); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker gains the ability to run code in the same process as the + Windows Update service (e.g. via COM interface call). +2. Attacker LoadLibraryW(L"Cbscore.dll") to satisfy the module test. +3. Attacker prepares a hard-link or junction from a Servicing target + file to an arbitrary protected file. +4. Service calls CreateActionListInternalExclusive(); mutex is skipped. +5. Service writes through the link with SYSTEM privileges. +6. Arbitrary file overwrite -> privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker who can co-load a DLL or otherwise invoke +Servicing APIs inside the Windows Update service context. + +Security Impact +-------------------------------------------------------------------- +Elevation of Privilege – attacker obtains SYSTEM-level write access to +arbitrary files, allowing full takeover of the operating system. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the unsafe implicit mutex bypass and replaces it with +an opt-in feature flag that is disabled by default. All code paths now +validate that a real mutex handle exists and is held for the duration of +critical file operations, eliminating the window where a crafted link +could be followed. No bypass has been identified in the new logic. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48799_wusys.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48799_wusys.dll.txt new file mode 100644 index 0000000..2de69b8 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48799_wusys.dll.txt @@ -0,0 +1,121 @@ +{'kb': 'KB5062553', 'patch_store_uid': 'a014e5bc-cfcb-48f5-83a9-3549e51e667d', 'confidence': 0.22, 'cve': 'CVE-2025-48799', 'change_count': 4, 'date': 1752036895.8282206, 'file': 'wusys.dll'} +-------------------------------------------------------------------- +CVE-2025-48799 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +WinStoreAuth runtime (wusys.dll) used by the Windows Update / Store +service stack. The affected functions are + • AuthenticationInternal::ExtractAccountId() + • AuthenticationInternal::ExtractProviderType() +Both are helper routines that translate IWebAccount* / +IWebAccountProvider* COM objects into internal identifiers. + + +Vulnerability Class +-------------------------------------------------------------------- +NULL-pointer dereference / missing input validation (CWE-476). The +crash occurs while dereferencing a user-supplied COM pointer that may +legitimately be NULL. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch, both helper functions assumed that the caller +passed a valid, non-NULL Microsoft::WRL::ComPtr object whose internal +pointer (*a1) is also non-NULL. They immediately dereferenced the +pointer to access the v-table, e.g. + v5 = (**a1)[6]; // ExtractProviderType() + v3 = ComPtr::As(a1,&v9); // ExtractAccountId() + +If *a1 == NULL the first memory access touches address 0x0 and the +process terminates with STATUS_ACCESS_VIOLATION. Because the code runs +inside service processes running as LocalSystem (Windows Update +service, Store service, etc.), an unprivileged client that can induce a +NULL IWebAccount*/IWebAccountProvider* causes the service to crash. + +Key variables and paths + a1 – pointer to caller-supplied ComPtr + *a1 – raw IWebAccount*/IWebAccountProvider* (may be NULL) + (**a1)[x] – v-table dereference that faults when *a1 == NULL + +The fault is reachable through higher-level helpers such as + CWUSystemInterface::GetAllAccountTickets() +which ultimately forward user-controlled parameters into the vulnerable +extractor routines without validation. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – ExtractAccountId +v3 = Microsoft::WRL::ComPtr<...>::As<...>(a1, &v9); // a1 used blindly + +// BEFORE – ExtractProviderType +v5 = (**a1)[6]; // *a1 may be NULL +``` +```c +// AFTER – added guard (AccountId example) +if (!Feature_NullAccountCrashFix_IsEnabled || *a1) { + v10 = 0; + v5 = ComPtr::As(a1, &v10); + ... +} else { + v4 = E_POINTER; // 0x80004003 + Return_Hr(...); + return v4; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged caller invokes a public API that eventually calls + GetAllAccountTickets(). +2. The call path enters + AuthenticationInternal::ExtractAccountId()/ExtractProviderType(). +3. Caller supplies a default-constructed ComPtr => *a1 == NULL. +4. Pre-patch code dereferences **a1, faults at address 0. +5. Service process running as SYSTEM crashes. + + +Attack Vector +-------------------------------------------------------------------- +Local code running in the user session that can reach the Windows Store +/ Update broker APIs passes a NULL IWebAccount* or IWebAccountProvider* +object. No special privileges are required to supply the malformed +parameter. + + +Patch Description +-------------------------------------------------------------------- +• Function signatures changed to receive a pointer to the ComPtr so the + internal raw pointer (*a1) can be validated first. +• Added feature-flag gates + Feature_NullAccountCrashFix + Feature_ExtractProviderTypeCrashFix + and early exits that return E_POINTER/0 when *a1 is NULL. +• Updated error-handling paths and unique Return_Hr() IDs (2749 => + 2761/2763 etc.) to distinguish the new failure cases. +• No other logic or data-structure changes were made. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, any user could crash the Windows Update / Store +service, leading to a persistent denial-of-service and possible loss of +update functionality. Because NULL-page mapping is blocked on modern +Windows, code execution is unlikely; the practical risk is service +instability and potential loss of defense-in-depth protections that the +service provides. + + +Fix Effectiveness +-------------------------------------------------------------------- +The explicit *a1 NULL checks fully remove the faulting code path when +the feature flag is enabled. No further dereferences occur without a +preceding validity check, so the immediate crash vector is resolved. +Residual risk: if the feature flag is disabled by policy the original +behavior returns; however, Microsoft typically enables such mitigation +flags for all supported SKUs, making the fix effective in practice. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48800_bdeunlock.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48800_bdeunlock.exe.txt new file mode 100644 index 0000000..895124b --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48800_bdeunlock.exe.txt @@ -0,0 +1,102 @@ +{'kb': 'KB5062553', 'confidence': 0.26, 'change_count': 1, 'cve': 'CVE-2025-48800', 'patch_store_uid': 'dcd36030-39a9-478e-bc2f-d15ad238a416', 'file': 'bdeunlock.exe', 'date': 1752036258.164755} +-------------------------------------------------------------------- +CVE-2025-48800 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows BitLocker user-mode unlock helper (bdeunlock.exe) – +CFveApiWrapper::GetStatusFlagsFromName() + +Vulnerability Class +-------------------------------------------------------------------- +Stack-based buffer overflow caused by structure size mismatch +(CWE-121 / CWE-693) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GetStatusFlagsFromName() obtains the BitLocker status of a volume by +calling the kernel FVE service: + 1. FveOpenVolumeW() – obtains a handle (v5) + 2. FveGetStatus() – fills a caller-supplied buffer with a + FVE_STATUS structure whose first DWORD must contain the total + size of that buffer. + +The function builds the request buffer on the stack in two disjoint +objects: + v6 – int[2] used as the structure header + v7 – char[?] intended to hold the remaining bytes + +Before the patch the code initialised them as follows: + memset(v7, 0, 0x80); // zero 128 bytes + v6[0] = 136; // declare 136 bytes to FVE + v6[1] = 9; // request id + +Because the advertised length (136) exceeded the actually cleared +memory (128) by 8 bytes, FveGetStatus() copied 8 extra bytes past the +end of the buffer, corrupting adjacent stack variables including the +saved non-volatile registers and, on x64, the chained exception +handler pointer. The overflow is small but sufficient to alter +control data and influence the program’s control flow when the +function returns. + +The drive path (a1) is attacker-controlled in several GUI and command +line flows, so a user with physical access to the target computer can +trigger the vulnerable routine merely by inserting a storage device +that causes bdeunlock.exe to run (e.g., the "unlock with USB" feature). +No additional privileges are needed. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable code (before) +memset_0(v7, 0, 0x80); // clears 128 bytes +v6[0] = 136; // tells FVE we have 136 bytes -> overflow +v6[1] = 9; +Status = FveGetStatus(v5, v6); + +// fixed code (after) +memset_0(v7, 0, 0x78); // clears 120 bytes +v6[0] = 128; // length now matches real buffer size +v6[1] = 9; +Status = FveGetStatus(v5, v6); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +CBdeUnlockUIController::OnListenedEvent() + ➜ _UnlockWithUSB() + ➜ CFveDriveType::CheckIsPortableDrive() + ➜ CFveApiWrapper::GetStatusFlagsFromName() ← overflow occurs + +Attack Vector +-------------------------------------------------------------------- +Physical attacker connects a storage device that causes BitLocker +unlock helper to query its encryption status. The malformed buffer is +built and overflows automatically; no special payload needs to be sent +through other channels. + +Patch Description +-------------------------------------------------------------------- +Microsoft reduced the advertised structure length from 136 (0x88) to +128 (0x80) bytes and correspondingly shortened the memset() to 0x78 +(120) bytes, bringing the actual buffer size, the cleared size, and +the size field passed to FveGetStatus() back into agreement. No other +logic was changed. + +Security Impact +-------------------------------------------------------------------- +The 8-byte overwrite occurs on the process stack of a signed, +auto-elevating system binary running as the invoking user. An +attacker can corrupt saved registers or frame pointers, potentially +leading to arbitrary code execution inside bdeunlock.exe. Since the +utility runs before BitLocker unlock, code execution can bypass the +intended pre-boot protection, yielding a BitLocker Security Feature +Bypass. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch fully removes the overflow by enforcing consistent structure +sizes. Provided FVE_STATUS remains 128 bytes on all supported builds, +the vulnerability is closed. No residual or alternate path using the +old size was observed in the diff. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48803_vbsapi.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48803_vbsapi.dll.txt new file mode 100644 index 0000000..95aa2ab --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48803_vbsapi.dll.txt @@ -0,0 +1,106 @@ +{'kb': 'KB5062553', 'change_count': 1, 'confidence': 0.14, 'patch_store_uid': '823407ea-2473-4a3c-b08f-d5037b8633a1', 'file': 'vbsapi.dll', 'cve': 'CVE-2025-48803', 'date': 1752036218.5698247} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +vbsapi.dll – routine SdbpGetVelocityState (exported by the +Virtualisation-Based Security run-time) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-353 – Missing Support for Integrity Check (logic / access–control +error) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SdbpGetVelocityState is an enclave-exposed helper that tells the caller +(1) whether the supplied *feature family* (a3 – L"Velocity" or +L"KIR") is supported, and (2) whether the requested *feature Id* (a4) +is currently enabled. The caller passes two write-back pointers: + int *FeatureKnown (a1) + BOOL *FeatureEnabled (a2) + +Before the patch the routine trusted the (family,Id) tuple blindly – +there is no cryptographic authentication, white-list validation, or +range checking apart from a hard-coded if/else list. Any Id that falls +outside those literal compares slips through the bottom of the tree +and the function still returns success (v10==1) while leaving +*FeatureKnown set to 0. Call-sites interpret that combination as +"known family, disabled feature" and silently switch the mitigation +off. Because the enclave boundary gives the caller (already executing +with user privileges in VTL1) the ability to invoke this routine, a +local attacker can supply a deliberately crafted, unauthenticated Id to +disable KIR / Velocity mitigations and elevate privileges inside the +secure world. + +The logic hole is purely a consequence of missing integrity support; +there is no signature, hash, or HMAC that protects the input. The +patch adds the previously omitted Id 0x036E9876 (57579638, +DoYouCopyFix) and tightens boundary conditions (>0x3554D8F became +>0x3554D90). As a side effect, the second out-parameter was promoted +from BOOL * to _DWORD * to make the 32-bit width explicit, but no other +behaviour changes. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch excerpts (de-compiled) +if (wcsicmp_0(a3,L"Velocity")) { + v10 = wcsicmp_0(a3,L"KIR") == 0; + goto LABEL_65; // <- returns success even when Id +} +... +if (v16 != 1) { +LABEL_65: + v9 = 0; // FeatureKnown = 0 + goto LABEL_66; // but v10 (function result) == 1 +} +``` +The function returns TRUE (success) although the supplied Id was never +validated. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode (inside a VBS enclave) -> SdbRetrieveAppCompatInfo -> +SdbpGetVelocityState + Pass arbitrary L"Velocity"/L"KIR" and rogue Id (a4) + Routine falls through the compare list -> LABEL_65 + Returns success, *FeatureKnown = 0, *FeatureEnabled = 0 + Caller disables the corresponding mitigation, gaining elevated + capabilities inside the enclave. + +Attack Vector +-------------------------------------------------------------------- +Local, post-compromise. Code already running inside a Virtualisation +Based Security enclave calls the exported API with a forged feature Id +that is outside the hard-coded list. + +Patch Description +-------------------------------------------------------------------- +1. Added explicit handling for Id 0x036E9876 (57579638, + Feature_DoYouCopyFix__private_IsEnabledDeviceUsageNoInline). +2. Changed upper-bound compare from 0x3554D8F to 0x3554D90, closing the + gap that previously let 0x3554D90 fall into the wrong branch. +3. Adjusted follow-on deltas and label numbers; removed dead + subtraction chain for 55922064. +4. Clarified width of the second out parameter by changing the type + from BOOL * to _DWORD *. +No additional functional changes were introduced. + +Security Impact +-------------------------------------------------------------------- +Before the fix an unprivileged enclave caller could forge an unchecked +feature Id, forcing the kernel to believe that a security mitigation +was disabled. This let the attacker undermine VBS isolation and gain +privileges within the trusted execution environment, which can be +leveraged for a full local elevation of privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The added compare closes the open range and the new constant covers the +previous hole, eliminating the unauthenticated paths. As the call now +fails for any non-white-listed Id, the integrity of the Velocity / KIR +state is preserved and the elevation vector is removed. No residual +bypass is observable in the patched logic. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48803_vertdll.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48803_vertdll.dll.txt new file mode 100644 index 0000000..98dc82c --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48803_vertdll.dll.txt @@ -0,0 +1,114 @@ +{'patch_store_uid': '140b6365-8efd-482c-91c1-75c13615cfcb', 'change_count': 2, 'date': 1752036207.3480744, 'confidence': 0.16, 'kb': 'KB5062553', 'cve': 'CVE-2025-48803', 'file': 'vertdll.dll'} +-------------------------------------------------------------------- +CVE-2025-48803 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +vertdll.dll (user-mode runtime loaded into Windows VBS enclaves). +Affected routine: RtlpEnterCriticalSectionContended (offset +0x180009FF0). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-353: Missing support for integrity check / use of untrusted shared +state inside an enclave. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Inside VBS user-mode enclaves the trusted system supplies a read-only +alias called RtlEnclaveUntrustedSharedUserData that mirrors selected +fields from KUSER_SHARED_DATA while preventing the enclave from +accessing attacker-controlled pages. Prior to the patch the critical +section helper RtlpEnterCriticalSectionContended read the value at the +absolute virtual address 0x7FFE036A: + + if (MEMORY[0x7FFE036A] > 1u) { ... } + +Because the enclave address space is fully controlled by the untrusted +host process, the host can map writable memory at 0x7FFE0000 inside the +enclave. Consequently the test operates on attacker-supplied data with +no integrity guarantee. The 16-bit field governs two internal +variables: + + v3 – spin-count history (low 24 bits of CSR->Flags) + v4 – "adaptive spin" flag (bit 0x2000000 of CSR->Flags) + +When the field is forced to a value <=1 the optimisation path is +skipped; when forced >1 it is taken. By flipping the bit at run time an +attacker can: + +1. Force excessive or negative spin counts (v3 becomes 0 or a large + attacker-chosen number). +2. Drive the state machine in RtlpSpinForCriticalSection and subsequent + atomic sequences into unexpected transitions. +3. Leave the CRITICAL_SECTION structure in an inconsistent state + (Owner/LockCount mismatches) that is later processed by kernel + callbacks (NtAlertThreadByThreadId, NtContinue, etc.). + +Because enclave code runs at process integrity but manages objects that +are subsequently unmarshalled by the kernel on exit, corruption of the +structure results in an Elevation of Privilege from the enclave to the +hosting process context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (MEMORY[0x7FFE036A] > 1u) // untrusted alias +{ + v3 = *(QWORD *)(a1 + 32) & 0xFFFFFF; + v4 = (*(QWORD *)(a1 + 32) & 0x2000000) != 0; +} + +// after +if (*(WORD *)(RtlEnclaveUntrustedSharedUserData + 874) > 1u) // trusted +{ + v3 = *(QWORD *)(a1 + 32) & 0xFFFFFF; + v4 = (*(QWORD *)(a1 + 32) & 0x2000000) != 0; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Enclave or host code calls EnterCriticalSection on a contended lock. +2. RtlpEnterCriticalSectionContended executes inside the enclave. +3. Routine reads WORD at 0x7FFE036A taken from enclave-mapped memory. +4. Host furnishes crafted value (>1) via a writable page mapping. +5. v3/v4 are set to attacker-chosen state; spin loop and atomic + adjustments proceed with inconsistent counters. +6. Lock state becomes corrupted; subsequent leave/unwind paths execute + incorrect atomic updates, mis-adjusting LockCount and recursion + depth. +7. On enclave exit, corrupted CRITICAL_SECTION surfaces in kernel APC + delivery, enabling arbitrary code execution in the host process. + +Attack Vector +-------------------------------------------------------------------- +Local attacker that can load or interact with a VBS enclave maps a +writable page at VA 0x7FFE0000 inside the enclave before the vulnerable +routine executes, then writes a value >1 at offset 0x36A. No elevated +privilege is required to perform the mapping. + +Patch Description +-------------------------------------------------------------------- +The patch replaces the hard-coded absolute address with a reference to +RtlEnclaveUntrustedSharedUserData, the officially supported alias that +is mapped read-only inside the enclave and whose contents are populated +and integrity-checked by the kernel. This removes attacker control of +the field. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker in the same user session could escape the +VBS enclave boundary and execute arbitrary code in the host process +context (Elevation of Privilege). Exploitation does not require admin +rights and affects any system using VBS enclaves. + +Fix Effectiveness +-------------------------------------------------------------------- +By redirecting the read to a kernel-managed, read-only alias the patch +eliminates the untrusted data source and closes the attack surface. No +further logic changes were necessary. Residual risk is low, though a +full audit should confirm that no other enclave routines still read +0x7FFEXXXX directly. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48804_fvevol.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48804_fvevol.sys.txt new file mode 100644 index 0000000..c003cf1 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48804_fvevol.sys.txt @@ -0,0 +1,124 @@ +{'cve': 'CVE-2025-48804', 'date': 1752036514.3144295, 'patch_store_uid': '546cc225-5ff3-4cd7-be6d-d4d62c9a8c45', 'kb': 'KB5062553', 'confidence': 0.18, 'change_count': 42, 'file': 'fvevol.sys'} +-------------------------------------------------------------------- +CVE-2025-48804 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft BitLocker driver (fvevol.sys) – ICE (Inline Crypto Engine) +key-management and hardware-wrapping support code. + +Vulnerability Class +-------------------------------------------------------------------- +Acceptance of Extraneous Untrusted Data with Trusted Data (CWE-349) +leading to a security-feature bypass. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several IOCTL and internal helper paths that create, verify, or insert +hardware-wrapped FVEKs (Full-Volume Encryption Keys) trusted the caller +supplied buffer length and key-size fields without checking them against +what the active ICE Key-Management Interface (KMI) actually supports. + +Old behaviour (examples): + • SimIceKmiWrapKey() accepted *any* requested clear-key size up to + 0x20 bytes irrespective of the capability of the attached KMI and + copied caller data straight into the wrapping request. + • SimIceKmiInsertChallenge() accepted any challenge length up to 0x20 + bytes and any output buffer >=0x118 without checking KMI limits. + • FveIsValidWrapKeyRequest() only compared the caller-provided lengths + internally, it never queried KMI capabilities and therefore could + be tricked by oversized values that over-indexed later memmove. + • FveIceKmiValidateUnwrapKey(), FveIceVerifyHwWrappedFvek() and + FveIceHwWrapFvek() trusted (*WORD)v5 and similar size fields and + performed arithmetic that could wrap or allocate undersized pools. + +Because the KMI layer later calculated pool sizes from these unchecked +lengths, an attacker able to issue the IOCTL_FVE_HARDWARE_WRAP_FVEK or +communicate with the ICE KMI could supply an over-large key/challenge +length. The driver then: + 1. Allocated a buffer that was too small (integer-truncation to + unsigned ‑> wrap at 0xFFFF, or bit size not supported by ICE). + 2. memmove()’d attacker data beyond the end of that buffer, or + copied fewer bytes than expected into the device, leaving the rest + attacker-controlled but treated as *trusted*. + 3. The corrupted structure was handed to the hardware for key + wrapping/unwrapping, bypassing BitLocker policy (e.g. FIPS mode, + KMI capabilities → allows 256-bit AES, attacker supplies 512-bit + and forces wrap/unwrapped key that the policy should forbid). + +Parameters/structures implicated + • struct ICE_KMI_WRAP_KEY_REQUEST { WORD Total; WORD HeaderLen; + WORD WrappingType; … } + • (*WORD)v5 in FveIceKmiValidateUnwrapKey – attacker-controlled key + length. + • cbInput/keySize in FveCreateIceKey – calculated from unchecked + a4/a5 bit-size. + +Patch Description +-------------------------------------------------------------------- +1. Each entry point now queries KMI capabilities first + (SimIceKmiQueryCapabilities / FveIceKmiGetCapabilities) and rejects + any request whose size exceeds the advertized maximum. +2. Additional integer-overflow checks added: + if (v5+24 < v5) return STATUS_INTEGER_OVERFLOW; +3. Buffer allocations changed from sizeof(struct) (56) to 64 to ensure + space for future-proof fields. +4. After use, allocated buffers are wiped with memset(0) and released. +5. FveDatumHwFvekGetWrappedTestKeyCopy introduced – copies wrapped key + into driver memory instead of using caller pointer directly. +6. FveMatchIceHwCryptoDescriptor completely rewritten – validates block + size, FIPS state, and ICE descriptor before accepting. +7. WPP trace-points updated; error paths hardened to early-return. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old SimIceKmiWrapKey +if (!a2 || !a3 || !a4 || *a3 > 0x20u) // only local checks + return STATUS_INVALID_PARAMETER; +... +memmove(a5+88, a2+24, *(WORD*)(a2+22)); // unchecked copy + +// patched +v8 = SimIceKmiQueryCapabilities(a1,&v10); +if (v8>=0) { + if (!a2 || ((BYTE4(v10)&1)&&(!a3 || *a3>(WORD)v10))) + return STATUS_INVALID_PARAMETER; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode ⇒ DeviceIoControl(Fvevol, IOCTL_FVE_HW_WRAP_FVEK, …) + ↳ IoctlFveHardwareWrapFvek() + ‑ copies caller buffer to pool without real validation + ↳ FveIsValidWrapKeyRequest() (old: size only) + ↳ FveIceHwWrapFvek() + ↳ SimIceKmiWrapKey()/SimIceKmiInsertChallenge() + (unchecked memmove) ⇒ corrupted request + ↳ Hardware wraps key ⇒ policy bypass. + +Attack Vector +-------------------------------------------------------------------- +Local adversary with physical or administrative access sends crafted +IOCTL_FVE_HARDWARE_WRAP_FVEK request containing oversized key/challenge +fields. No authentication is required beyond ability to issue the +IOCTL; attack can be staged from WinPE or DMA-capable bus. + +Security Impact +-------------------------------------------------------------------- +By injecting extraneous data the attacker coerces BitLocker to accept +or generate hardware-wrapped FVEKs outside the allowed cryptographic +parameters, effectively disabling FIPS/key-size enforcement and +bypassing BitLocker’s hardware key-wrapping protections. This enables +physical attacks that retrieve or substitute the volume master key and +thus decrypt data without the authorised key protectors. + +Fix Effectiveness +-------------------------------------------------------------------- +Patch adds capability-based size checks, integer-overflow guards, +secure buffer handling, and full cleanup, eliminating the original +unchecked memmove paths. With these validations in place the crafted +oversized requests are rejected with STATUS_INVALID_PARAMETER or +STATUS_BUFFER_TOO_SMALL, preventing the bypass. No bypass of the new +checks is apparent from the diff. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48805_msmpeg2vdec.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48805_msmpeg2vdec.dll.txt new file mode 100644 index 0000000..96fdff3 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48805_msmpeg2vdec.dll.txt @@ -0,0 +1,120 @@ +{'kb': 'KB5062553', 'patch_store_uid': 'beb8cab8-c498-45e4-a8ed-1dc1a4facc87', 'file': 'msmpeg2vdec.dll', 'change_count': 32, 'confidence': 0.27, 'cve': 'CVE-2025-48805', 'date': 1752037727.5283873} +-------------------------------------------------------------------- +CVE-2025-48805 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft MPEG-2 Video Extension (msmpeg2vdec.dll) – internal work +queue management routines + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based Buffer Overflow (CWE-122) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The decoder maintains a singly-linked list that queues per-picture +work items. Each list element is an allocator–backed structure whose +link field is located at offset +0x160 (decimal 352) and a reference +counter at +0x150 (decimal 336). + +The enqueue helper originally implemented as sub_1800D3630() + + • acquired the object’s critical section (a1+0x2038) + • blindly appended the node (a2) to the tail of the list whose head + resides at a1+0x2020 + • released the lock and returned 0 + +No verification was performed to guarantee that the same node was not +already present in the list. If higher-level decoding logic called +this helper twice with the same a2 pointer (a legitimate scenario when +error-handling or re-entrancy occurs) the following corruption +resulted: + + – On the second call *(a2+352) is reset to NULL while another list + entry still references a2, breaking the forward chain. + – Subsequent traversals operate on freed or out-of-range memory, + allowing arbitrary pointer reads/writes. + – Because list elements are heap-allocated MPEG-2 picture + structures, an out-of-bounds write occurs inside the process + heap, leading to controllable memory corruption and ultimately + code execution in the calling context (typically the media + player sandbox or the hosting browser tab). + +This fault is completely inside the Microsoft-supplied extension – no +kernel interaction is involved – but it is triggered by data that an +attacker supplies in a crafted MPEG-2 bit-stream. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* before – sub_1800D3630 (excerpt) */ +_InterlockedIncrement((volatile LONG *)(a2 + 336)); +*(DWORD *)(a2 + 40) = 0; +*(QWORD *)(a2 + 352) = 0; // zero link field +// blindly append +for ( i = *(QWORD *)(head + 352); i; i = *(QWORD *)(i + 352) ) + tail = i; +*(QWORD *)(tail + 352) = a2; // duplicate insertion possible +``` +```c +/* after – sub_1800D5428 (excerpt) */ +if ((BYTE)sub_1800D6960(flag)) { + for (i = *head; i; i = *(QWORD *)(i + 352)) { + if (i == a2) { // already in list ? + v2 = -1; // signal error + goto leave; + } + } +} +// append only when unique +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. MPEG-2 parser processes crafted picture headers. +2. Decoder worker allocates a picture struct P and queues it via + sub_1800D3630(). +3. Malformed stream triggers an error path that re-queues the same P + without having been dequeued first. +4. Duplicate insertion corrupts the forward list. +5. A later dequeue or flush walks the chain, overruns heap memory and + transfers control to attacker-supplied data. + +Attack Vector +-------------------------------------------------------------------- +A malicious MPEG-2 video file or streaming content played through any +Windows component that leverages msmpeg2vdec.dll. No user +interaction beyond opening/previewing the media is required. The +attack works in the default configuration and in the browser preview +handler. + +Patch Description +-------------------------------------------------------------------- +The update replaces sub_1800D3630 with sub_1800D5428 which: + + • Enumerates the current queue while the critical section is held. + • If the candidate node is already present, aborts and returns –1. + • Otherwise continues with the original append logic. + • Propagates the error status to callers by changing the return type + (unsigned int instead of constant 0). + +Ancillary changes in sub_1800E4060() and sub_1800D8030() only rename +globals and tighten feature-flag gating; they do not affect the fix. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could achieve heap corruption that +reliably leads to remote code execution in the context of the calling +process. Successful exploitation bypasses CFG/DEP because the attacker +controls both the overwritten forward pointer and subsequent data +written by the list walker. + +Fix Effectiveness +-------------------------------------------------------------------- +The duplicate-node detection fully prevents the corrupted list state +that caused the overflow. Because the verification is performed while +the critical section is held, race-conditions are excluded. Returning +an explicit error allows higher-level code to recover gracefully. +No further exploitation path was identified after the fix. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48806_msmpeg2vdec.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48806_msmpeg2vdec.dll.txt new file mode 100644 index 0000000..5b312a6 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48806_msmpeg2vdec.dll.txt @@ -0,0 +1,121 @@ +{'date': 1752036862.3634894, 'patch_store_uid': 'beb8cab8-c498-45e4-a8ed-1dc1a4facc87', 'file': 'msmpeg2vdec.dll', 'change_count': 32, 'cve': 'CVE-2025-48806', 'kb': 'KB5062553', 'confidence': 0.26} +-------------------------------------------------------------------- +CVE-2025-48806 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft MPEG-2 Video Extension (msmpeg2vdec.dll) – specifically the +sample management code that maintains an internal singly-linked list at +decoder-context offset 0x2020 (8224). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (double insertion of a list element leaves a +stale pointer that is later dereferenced). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Function sub_1800D3630 (renamed sub_1800D5428 after the patch) is +invoked whenever a decoded sample (pointer a2) is queued on the +"ready" list that hangs off context->+0x2020. Each sample embeds a +next-pointer at +0x160 (352) and a reference counter at +0x150 (336). + +PRE-PATCH behaviour: + 1. Caller passes context (a1) and sample (a2). + 2. The critical section at context+0x2038 (8232) is entered. + 3. The code blindly appends a2 to the tail of the list without first + checking whether the sample is already linked. + 4. Because the reference count is incremented every time the routine + is called, the same object can be inserted multiple times, + producing identical list nodes that share the same memory. + +REMOVAL path (not shown in the diff) only unlinks *one* occurrence and +releases one reference. The second list entry continues to hold the +now-dangling pointer. Subsequent list walks eventually reach this +pointer and dispatch a virtual method situated 48 bytes into the freed +object (see the original sub_180191FA0), leading to a call through a +freed vtable. An attacker who controls heap layout can replace the +freed block, pivoting execution to attacker-controlled data. + +The helper sub_180191FA0 (now moved to sub_180192A80) shows exactly +that dereference pattern: it reads context+0x520 (1312) and calls +(*(void **)vtable+0x30)(obj). If obj has already been freed, code +execution follows attacker data. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// PRE-PATCH: sub_1800D3630 (extract) +EnterCriticalSection(cs); +_InterlockedIncrement(a2 + 336); +*(DWORD *)(a2 + 40) = 0; +*(QWORD *)(a2 + 352) = 0; // reset next +if (ctx->Head) // no duplicate check +{ + node = ctx->Head; + while (node->Next) // walk to tail + node = node->Next; + node->Next = a2; // append sample +} +else +{ + ctx->Head = a2; // first element +} +LeaveCriticalSection(cs); +``` + +```c +// POST-PATCH: duplicate suppression +if (sub_1800D6960(g_Config)) // returns 1 when protection is on +{ + for (node = ctx->Head; node; node = node->Next) + if (node == a2) // already in list -> abort + { + status = -1; + goto exit; + } +} +``` +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Malicious MPEG-2 bitstream -> Media Foundation pipeline -> +CDecoder::QueueSample() -> sub_1800D5428 (AddToReadyList) – sample is +inserted twice – later CDecoder::ProcessSamples() walks the list and +calls sub_180192A80 on the stale node, executing freed memory. + +Attack Vector +-------------------------------------------------------------------- +A crafted MPEG-2 video file or stream rendered by any application that +loads the Microsoft MPEG-2 Video Extension (e.g., Movies & TV, Edge, +Media Foundation-based players). No special privileges are required; +code runs in the context of the invoking process. + +Patch Description +-------------------------------------------------------------------- +1. Added helper sub_1800D6960 that retrieves a configuration flag and + returns its LSB. +2. In sub_1800D5428 the flag gates a pre-insertion search that scans + the ready-list for the incoming sample pointer. If found, the + function aborts and returns -1, preventing a double insertion. +3. Legacy clean-up code (previously in sub_180191FA0) was moved to + sub_180192A80; functionality unchanged. + +Security Impact +-------------------------------------------------------------------- +Without the patch an attacker can gain arbitrary code execution inside +any process that decodes MPEG-2 content. Successful exploitation may +lead to full user-level compromise, sandbox escape, or elevation via a +privileged media service, depending on the host application. + +Fix Effectiveness +-------------------------------------------------------------------- +The duplicate-insertion check removes the immediate UAF condition and +is executed under the same critical section, so no race window +remains. Protection, however, depends on the runtime flag parsed by +sub_1800D6960; if the flag is disabled (unknown default), the list +remains unaudited. No further issues were visible in the supplied +patch, but additional auditing and fuzzing of list operations is +recommended. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48808_ntoskrnl.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48808_ntoskrnl.exe.txt new file mode 100644 index 0000000..6c16732 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48808_ntoskrnl.exe.txt @@ -0,0 +1,113 @@ +{'change_count': 508, 'confidence': 0.27, 'kb': 'KB5062553', 'date': 1752037959.6627135, 'cve': 'CVE-2025-48808', 'patch_store_uid': '877325f0-3361-4f8e-830a-96d6ce09ca35', 'file': 'ntoskrnl.exe'} +-------------------------------------------------------------------- +CVE-2025-48808 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Event Tracing for Windows (ETW) – kernel-mode parser routine +EtwpValidateFlagExtension() in ntoskrnl.exe + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read / information disclosure caused by missing lower +bound validation (CWE-200 / CWE-125) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The ETW flag-extension header that precedes every EVENT_TRACE_HEADER +stores an offset (negative value stored in a1[18]) that points from +the start of the header to a variable-length list of flag/length +pairs. Prior to the patch the routine EtwpValidateFlagExtension() +validated that + • the high byte (BYTE2(v1)) equals 0xFF, and + • total buffer size >= offset + 4, and + • *EXT->Count * 4 <= remaining buffer. + +Crucially it never verified that the *offset* itself is large enough +to be inside the fixed EVENT_TRACE_HEADER (0xB0 bytes) nor that the +list item-count is non-zero. An attacker could therefore supply a +tiny but non-zero offset (e.g. 0x04) and a crafted Count value that +makes all subsequent size checks pass. When the for-loop iterates +through the user–controlled “length” fields the code indexes past the +end of the caller-supplied buffer and starts reading adjacent kernel +memory, byte by byte, until the artificially constructed counter +(v6) underflows to zero. + +Because EtwpValidateFlagExtension() returns STATUS_SUCCESS, the +caller treats the extension as valid and copies the over-read data +back to user space via ETW control APIs, disclosing uninitialised or +sensitive kernel memory to an un-privileged attacker. + +Structures / parameters affected + a1 -> EVENT_TRACE_HEADER (user supplied) + a1[18] : negative offset to FLAG_EXTENSION (attacker controlled) + FLAG_EXTENSION->Count / Length : attacker controlled, drives read + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (simplified) +v3 = *a1; // total buffer size +v4 = (USHORT *)((char*)a1 + (USHORT)v1); +if (*v4 && 4 * *v4 <= v3 - (USHORT)v1) { + v5 = v4 + 2; + v6 = *v4 - 1; // remaining size counter + for (i = 0; i < v4[1]; ++i) { + if (!v6) return STATUS_BUFFER_TOO_SMALL; + v8 = *v5; // user controlled length + if (v6 < v8) return STATUS_BUFFER_TOO_SMALL; + v6 -= v8; // underflow possible + v5 += 2*v8; // out-of-bounds read here + } + if (!v6) return STATUS_SUCCESS; +} +``` +```c +// after patch (excerpt) +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_71()) { + if (BYTE2(v1) != 0xFF || (USHORT)v1 < 0xB0) + return STATUS_BUFFER_TOO_SMALL; // new lower bound check +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens an ETW session and supplies an EVENT_TRACE_PROPERTIES + buffer with FlagExtensionOffset < 0xB0 and crafted list fields. +2. kernel -> EtwpValidateFlagExtension() +3. Missing lower-bound check allows pointer to land inside + kernel-controlled memory. +4. for-loop reads beyond caller buffer, copying kernel data into the + ETW reply. +5. User receives the buffer and extracts leaked memory. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running in user mode. Requires the +ability to start or control an ETW session (TraceSetInformation / +StartTrace, available to normal users by default). + +Patch Description +-------------------------------------------------------------------- +Microsoft added strict lower-bound validation on the offset value +((USHORT)v1 >= 0xB0) when the mitigation feature flag is active and +mirrored the same logic for the legacy path. Early exits now return +STATUS_BUFFER_TOO_SMALL or STATUS_DATATYPE_MISALIGNMENT before any +pointer arithmetic occurs, completely preventing the out-of-bounds +read. + +Security Impact +-------------------------------------------------------------------- +The flaw allows disclosure of up to several kilobytes of uninitialised +kernel memory per request. An attacker may repeatedly query ETW to +harvest sensitive information such as kernel pointers or memory +contents, facilitating further exploitation (KASLR bypass, credential +material, etc.). + +Fix Effectiveness +-------------------------------------------------------------------- +The added lower-bound check removes the ability to supply an offset +that targets kernel memory; all subsequent calculations operate on a +validated, in-bounds pointer. No alternative path that re-introduces +the out-of-bounds read remains, therefore the patch is effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48811_vbsapi.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48811_vbsapi.dll.txt new file mode 100644 index 0000000..e3b0124 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48811_vbsapi.dll.txt @@ -0,0 +1,134 @@ +{'date': 1752036288.3073962, 'patch_store_uid': '823407ea-2473-4a3c-b08f-d5037b8633a1', 'file': 'vbsapi.dll', 'change_count': 1, 'kb': 'KB5062553', 'confidence': 0.19, 'cve': 'CVE-2025-48811'} +-------------------------------------------------------------------- +CVE-2025-48811 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows VBSAPI user-mode library (vbsapi.dll) – routine +SdbpGetVelocityState, address 0x1800121C0. The helper is used by the +kernel and VBS enclaves to query the state of a Velocity / KIR feature +flag. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / Missing integrity check (CWE-353) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine signature is + + BOOL8 SdbpGetVelocityState( + int *FeatureKnown, // a1 – out + BOOL *FeatureEnabled, // a2 – out (vulnerable build) + PCWSTR GroupName, // a3 – "Velocity" | "KIR" + DWORD FeatureId) // a4 – user controlled + +For the string "Velocity" the code executes a hand-written switch made +of nested "if (a4 – const)" blocks that call one of the internal +Feature_XXXX_IsEnabledDeviceUsageNoInline() helpers and forward the +return value through *FeatureEnabled (v4). At the end of the function + + *FeatureKnown = v9; // 1 if id recognised + *FeatureEnabled = v4; // helper result + return v10; // group recognised + +A logic error placed the pivot constant one step too low: + + if (a4 > 0x3554D8F) // 0x3554D8F == 55922063 + { … } + +The very next recognised Velocity id is 55922064. Because the upper +limit was off by one, FeatureId 55922064 (and every id that relied on +code located in the lower branch) was routed into a block that never +contained its handling case, causing execution to drop through to the +failure label (LABEL_65). At that point + + v9 = 0; // function claims id is unknown + // v4 keeps its previous value (initially 0) + +but no integrity helper is executed, so the state of the feature is +never checked. An enclave or user-mode caller can therefore request +Velocity id 55922064 and receive a positive "group recognised" return +value while the operating system has not performed the mandatory +validation of the feature flag – an integrity break that enables a +local attacker to tamper with Velocity/KIR policy decisions inside the +secure VTL1 context and elevate privilege. + +Secondary issue – type mismatch: +The vulnerable build declares the second out parameter as BOOL* (alias +LONG, 32-bit) yet some enclave headers declared it as C++ bool* (8-bit) +which made the callee overwrite three adjacent bytes inside the secure +stack frame. The patch changes the prototype to _DWORD* to enforce a +32-bit store and avoid structure layout mismatches. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable build +if (a4 > 0x3554D8F) { // wrong pivot (55922063) + ... // no case for 55922064 here +} +... +if (a4 == 55922064) { // unreachable – dead code + IsEnabledDeviceUsageNoInline = + Feature_CompatPreallocatedVelocity55922064__private_ + IsEnabledDeviceUsageNoInline(); + goto LABEL_16; +} +``` +```c +// patched build +if (a4 > 0x3554D90) { // pivot raised to 55922064 + ... +} +... +if (a4 == 55922064) { + IsEnabledDeviceUsageNoInline = + Feature_CompatPreallocatedVelocity55922064__private_ + IsEnabledDeviceUsageNoInline(); + goto LABEL_16; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker executes in VTL0 and calls SdbpGetVelocityState with + GroupName = "Velocity" and FeatureId = 55922064. +2. The pivot check mis-routes execution; no helper is executed. +3. Function exits with *FeatureKnown = 0, but returns TRUE (group + recognised) and *FeatureEnabled unchanged. +4. Enclave logic that only tests the return value treats the feature as + successfully validated and enables privileged functionality. + +Attack Vector +-------------------------------------------------------------------- +Local attacker able to run code that can interact with a VBS enclave or +any component that trusts SdbpGetVelocityState can supply a crafted +Velocity FeatureId (e.g. 55922064) and bypass the missing integrity +check, gaining higher privileges inside the secure world. + +Patch Description +-------------------------------------------------------------------- +1. Pivot constant raised from 0x3554D8F to 0x3554D90 so that the block + that actually contains the case for 55922064 is reached. +2. Several branches reshuffled; missing case for id 55922064 is now + reachable and executed. +3. New upper boundary (0x36E9876) and id 57579638 (DoYouCopyFix) added. +4. Signature changed from BOOL* to _DWORD*; local variable v4 switched + from BOOL to int, eliminating potential size mismatches. + +Security Impact +-------------------------------------------------------------------- +The missing integrity check allows an authorised but unprivileged local +user to trick the VBS subsystem into treating an unchecked Velocity/KIR +feature as valid, effectively disabling intended policy protection and +enabling privilege escalation across the enclave boundary. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated constant steers FeatureId 55922064 (and surrounding ids) +into the correct branch where the helper is invoked, restoring the +integrity check path. The prototype change removes the type-size +ambiguity. No residual bypass was found in the patched code, so the +fix is considered complete. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48811_vertdll.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48811_vertdll.dll.txt new file mode 100644 index 0000000..db14b1a --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48811_vertdll.dll.txt @@ -0,0 +1,117 @@ +{'date': 1752036256.3771136, 'change_count': 2, 'cve': 'CVE-2025-48811', 'confidence': 0.17, 'kb': 'KB5062553', 'file': 'vertdll.dll', 'patch_store_uid': '140b6365-8efd-482c-91c1-75c13615cfcb'} +-------------------------------------------------------------------- +CVE-2025-48811 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +vertdll.dll (VBS Enclave run-time), function +RtlpEnterCriticalSectionContended + +Vulnerability Class +-------------------------------------------------------------------- +CWE-353 Missing Support for Integrity Check (trusting untrusted +shared-memory content) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RtlpEnterCriticalSectionContended is the enclave implementation of the +NT critical-section slow-path. Before the patch the routine decided +whether a critical section should use the adaptive spin logic by +reading the system logical-processor count from absolute address +0x7FFE036A: + + if (MEMORY[0x7FFE036A] > 1) + { use spin counters } + +0x7FFE0000 – 0x7FFE0FFF is the user-mode shared data page that the +kernel maps read-only for normal processes. Inside a VBS enclave this +page is **not measured or protected**; the host process (running +outside the enclave and therefore untrusted) supplies the backing +page during the #VE (#PF-intercept) that occurs on first access. + +Because the enclave code treated the value at 0x7FFE036A as trusted, a +malicious host could return any 16-bit value. When the value is forced +>1, two effects follow: + +1. v4 is set, enabling the path that continuously adjusts the high + byte and the lower 24 bits of the SpinCount/Flags field at offset + +0x20 of the RTL_CRITICAL_SECTION struct referenced by a1. + +2. v3 is loaded with the lower 24 bits of the same field and is later + incremented/decremented without any bounds check when v4 is true. + +Because the critical-section struct is supplied by the untrusted host +(the enclave merely receives a pointer), unverified arithmetic on that +field allows the host to make the enclave **write an attacker-chosen +24-bit value** back to a1+0x20. The struct resides outside the +enclave, so the write crosses the trust boundary and constitutes an +integrity violation. If the host maps that memory over sensitive data +(e.g., another enclave page or a host-controlled object) the enclave +performs a privileged write on behalf of the attacker, enabling local +privilege escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable read – trusts shared user data +if (MEMORY[0x7FFE036A] > 1u) +{ + v3 = *(_QWORD *)(a1 + 32) & 0xFFFFFFi64; + v4 = (*(_QWORD *)(a1 + 32) & 0x2000000i64) != 0; +} + +// patched read – explicit enclave helper marks data as untrusted +if (*(_WORD *)(RtlEnclaveUntrustedSharedUserData + 874) > 1u) +{ + v3 = *(_QWORD *)(a1 + 32) & 0xFFFFFFi64; + v4 = (*(_QWORD *)(a1 + 32) & 0x2000000i64) != 0; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Host process enters a VBS enclave that subsequently calls + RtlEnterCriticalSection on a host-allocated RTL_CRITICAL_SECTION. +2. The first attempt to acquire the lock fails, redirecting to + RtlpEnterCriticalSectionContended. +3. Function dereferences 0x7FFE036A; page fault exits enclave (#VE). +4. Host returns crafted page with NumberOfProcessors > 1. +5. Enclave sets v4=TRUE and later writes manipulated value v10 back to + a1+0x20, modifying host memory with enclave trust level. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privilege attacker able to create or load a VBS enclave +(gaining code-execution inside it) and control the memory layout +outside the enclave. By supplying a forged shared-data page the +attacker provokes an unchecked write from the enclave into arbitrary +host-process memory, leading to privilege escalation. + +Patch Description +-------------------------------------------------------------------- +The single-line change replaces the hard-coded absolute pointer +0x7FFE036A with RtlEnclaveUntrustedSharedUserData + 874. The helper +symbol resolves, at build time, to the enclave API that performs the +mandatory integrity segregation between trusted enclave pages and +untrusted host-supplied pages. Effectively the patch: + +1. Uses the documented enclave gateway for shared user data. +2. Explicitly labels the data as untrusted, disabling any write-backs + based on that information. +3. Removes the implicit trust of host-controlled content. + +Security Impact +-------------------------------------------------------------------- +Before the fix a host process could coerce enclave code to issue an +arbitrary 24-bit write outside the enclave boundary, enabling local EoP +from user-mode to the more-privileged enclave context or to SYSTEM if +a privileged enclave is loaded. + +Fix Effectiveness +-------------------------------------------------------------------- +The new access path funnels the read through the enclave runtime, which +puts the page in the untrusted mapping and forbids speculative use of +its contents for integrity-sensitive decisions. No direct dereference +of 0x7FFEXXXX survives, eliminating the primitive used for the attack. +Regression or alternative bypass is not visible in the diff. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48815_ssdpsrv.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48815_ssdpsrv.dll.txt new file mode 100644 index 0000000..d541d11 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48815_ssdpsrv.dll.txt @@ -0,0 +1,122 @@ +{'kb': 'KB5062553', 'file': 'ssdpsrv.dll', 'change_count': 16, 'confidence': 0.18, 'patch_store_uid': '98639a85-78bf-46dc-98f7-63d18b92622c', 'cve': 'CVE-2025-48815', 'date': 1752037491.5536797} +-------------------------------------------------------------------- +CVE-2025-48815 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows SSDP Service (ssdpsrv.dll) – RPC helpers that manage client +notification semaphores: GetNotificationRpc, RemoveSyncHandle, +WakeupGetNotificationRpc, InitializeSyncHandle and +RegisterNotificationHelper. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-843 Access of Resource Using Incompatible Type (handle / type +confusion). Also an instance of "untrusted handle validation / +confused-deputy" leading to privilege elevation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The SSDP notification workflow is supposed to work with a semaphore +created by InitializeSyncHandle and owned by the server. The returned +handle is later passed back to several RPC calls so the service can +wait, signal or close it. + +In the original code the HANDLE parameters (named a1, hSemaphore, *a2 +etc.) are only tested for NULL: + if (!a1) return ERROR_INVALID_PARAMETER; +No further verification is made that the supplied handle really refers + • to a semaphore object, and + • to a semaphore previously issued by InitializeSyncHandle. + +Consequently any authenticated RPC client can transmit an arbitrary +HANDLE value. The service – running with service privileges – then +performs privileged actions on that foreign object: + • WaitForMultipleObjects(…, a1) (GetNotificationRpc) + • ReleaseSemaphore(a1, …) (Wakeup…) + • CloseHandle(a1) (RemoveSyncHandle) + +Because the kernel dispatches those syscalls according to the object +header referenced by the handle, supplying a handle of a different +object type constitutes a type confusion. Examples: + – Passing a handle to a process or thread makes the service close it + or wait on it, potentially disturbing security-critical objects. + – Passing a duplicated high-privilege token handle and getting it + closed by the service can later allow the attacker to re-open the + underlying kernel object and obtain escalated rights (classic + handle squatting / EoP scenario). + +No membership or ownership tracking existed – the internal lists +qword_18004DB28 / qword_18004DB58 kept only CSsdpNotifyRequest objects +and were never consulted for semaphore validity. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – no validation at all +if (a1 && a2) { + Handles[0] = ShutDownEvent; + Handles[1] = a1; // attacker-controlled + WaitForMultipleObjects(2, Handles, …); + … +} + +// after – accept only registered semaphores +if (!a1 || !a2 || + !CSsdpNotifyRequestManager::IsNotifySemaphoreInList( + &CSsdpNotifyRequestManager::s_instance, 1, a1)) + return ERROR_INVALID_PARAMETER; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens/duplicates an arbitrary handle (or creates a handle + of a chosen type). +2. Attacker invokes any of the affected RPC methods and supplies that + handle instead of the legitimate notification semaphore. +3. SSDPSRV service uses the handle inside privileged APIs + (WaitForMultipleObjects / ReleaseSemaphore / CloseHandle). +4. Those operations are executed in the service’s security context and + affect a resource the attacker could not otherwise manipulate. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user able to call the SSDP RPC interface. +No special privileges are required beyond the ability to connect to the +\pipe\ssdpsrv named pipe. Successful exploitation yields local +Elevation of Privilege. + +Patch Description +-------------------------------------------------------------------- +• A new semaphore tracking list is maintained by + CSsdpNotifyRequestManager. +• InitializeSyncHandle now calls HrAddNotifySemaphoreToList to record + freshly created semaphores. +• All later RPC helpers call + IsNotifySemaphoreInList before using the handle; on failure they + return ERROR_INVALID_PARAMETER (87). +• RemoveSyncHandle additionally removes the entry via + HrRemoveNotifySemaphoreFromList. +• Legacy behaviour is kept behind a WIL feature flag so that the new + validation can be enabled selectively. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could coerce the SSDP service to operate +on any kernel object handle, leading to: + • unintended closing of privileged handles, + • semaphore signalling on attacker-chosen objects, + • or other undefined behaviour inside the kernel. +These primitives can be chained to obtain SYSTEM-level code execution +(Elevation of Privilege). + +Fix Effectiveness +-------------------------------------------------------------------- +The added positive list enforces strong handle provenance: only +semaphores created by the service are accepted. All dangerous code +paths now bail out early with ERROR_INVALID_PARAMETER when presented +with an unknown handle, removing the confused-deputy condition. +Residual risk: systems where the new WIL feature flag is disabled will +still follow the legacy path; effectiveness therefore depends on proper +roll-out of the feature enablement policy. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48819_upnphost.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48819_upnphost.dll.txt new file mode 100644 index 0000000..56c2258 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48819_upnphost.dll.txt @@ -0,0 +1,143 @@ +{'cve': 'CVE-2025-48819', 'patch_store_uid': 'a38814e4-f259-4c84-903d-d9b40fa9d663', 'kb': 'KB5062553', 'confidence': 0.26, 'change_count': 2, 'date': 1752037546.642898, 'file': 'upnphost.dll'} +-------------------------------------------------------------------- +CVE-2025-48819 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Universal Plug-and-Play (UPnP) Device Host – upnphost.dll. +The vulnerable code sits in the Windows-Internal-Lockdown (WIL) +feature-management helpers: + • FeatureImpl<…Feature_TestConfNum>::GetCurrentFeatureEnabledState() + • FeatureImpl<…Feature_TestValidate>::ReportUsage() +These helpers run inside the UPnP service process (Svchost, Local +Service account) and are reachable whenever the service evaluates or +reports feature state. + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / wrong function prototype, leading to copying of +uninitialised or freed kernel-mode heap data into a global cache that +is not locked or cleared (CWE-591 – Sensitive Data Storage in +Improperly Locked Memory). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. In GetCurrentFeatureEnabledState() (TestConfNum) the pre-patch code + tests additional policy bits by calling + + FeatureImpl<Feature_Servicing_CFONTPrintLeak>::__private_IsEnabled( + &Feature_TestValidate::impl); + + The template specialisation requested (CFONTPrintLeak) does **not** + match the object that is supplied (TestValidate). Because the two + feature-implementation structures have different internal layouts, + the callee interprets unrelated memory as its own state fields. + This causes: + • Reads of stale/freed heap memory. + • The read value to be copied into the global feature cache + maintained by the UPnP host. + +2. The same block immediately reports telemetry through + Feature_TestValidate::ReportUsage(). The implementation of that + method was declared as + + ReportUsage(_DWORD *impl, unsigned __int8 kind, …) + + while callers pass a full 64-bit value in the second register + (RDX). Because only the least-significant byte is consumed, the + upper seven bytes contain indeterminate stack content that is also + forwarded to wil::ReportUsageToService(). Consequently, memory from + the service’s stack – potentially containing privileged handles or + security tokens – is copied into WIL’s shared reporting buffers in + plain text. + +3. Those buffers are **never placed in locked pages**; they remain in + pageable, readable process memory long after use. Any code running + in the service context, or any Info-leak primitive that can map the + process, can harvest the leftover data and escalate privileges. + +Patch changes +• Replaced the wrong IsEnabled() call with a direct call to + Feature_TestValidate::ReportUsage(), eliminating the type confusion. +• Correctly widens temporary variables (v6, v9) to 64-bit, preventing + truncation of the feature-state mask. +• Redefines ReportUsage() to accept a 64-bit ReportingKind argument and + updates its internal stack layout, so no undefined bytes are + propagated. +• Removes dead logic that conditionally cleared bit 0x400 based on the + faulty IsEnabled() result. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// incorrect cross-type call (before) +if ((v9 & 0xC00) == 3072) +{ + if ((unsigned __int8) + FeatureImpl<Feature_Servicing_CFONTPrintLeak>::__private_IsEnabled( + &Feature_TestValidate::impl)) + … +} + +// fixed logic (after) +if ((v9 & 0xC00) == 3072 || (v9 & 0x40)) +{ + FeatureImpl<Feature_TestValidate>::ReportUsage( + Feature_TestValidate::impl, v9, 0xC00, v6); + … +} + +// ReportUsage() prototype mismatch +// before +__int64 ReportUsage(_DWORD *impl, unsigned __int8 kind, __int64 a3, __int64 a4); +// after +__int64 ReportUsage(unsigned int *impl, __int64 kind, __int64 a3, __int64 a4); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker invokes a UPnP API that causes the service to evaluate the + TestConfNum feature. +2. GetCurrentFeatureEnabledState() executes, enters the faulty branch + (v9 bits 0xC00 or 0x40 set). +3. __private_IsEnabled() is called with the wrong object → reads random + memory, writes it into the global cache. +4. ReportUsage() is called with a mismatched prototype → leaks stack + bytes into the same cache. +5. Attacker subsequently reads the cache (e.g., via another benign + API) and obtains privileged memory contents. + +Attack Vector +-------------------------------------------------------------------- +Exact public entry point is not shown in the diff. Any API that +reaches GetCurrentFeatureEnabledState() for Feature_TestConfNum can be +used (unknown). + +Patch Description +-------------------------------------------------------------------- +• Removes the mis-typed IsEnabled() call and instead calls + ReportUsage() on the correct feature. +• Corrects parameter and local-variable widths (32->64 bit). +• Rewrites Feature_TestValidate::ReportUsage() to take a 64-bit + ReportingKind and sets deterministic stack variables before passing + them to ReportUsageToService(). +• Initialises the telemetry-support buffer with a constant (3) instead + of leaving it zeroed. + +Security Impact +-------------------------------------------------------------------- +Before the patch, arbitrary kernel-pool or stack data could be +persisted in an unlocked, long-lived buffer inside the UPnP Device Host +service. A local or adjacent attacker able to trigger the feature-eval +path could harvest that data to bypass ASLR, discover privileged +pointers, or steal authentication material, and subsequently mount an +Elevation-of-Privilege attack. + +Fix Effectiveness +-------------------------------------------------------------------- +The incorrect template invocation has been eliminated and the function +signatures now match their call sites, removing both the type +confusion and the uninitialised register leakage. No residual paths +invoking CFONTPrintLeak methods with a TestValidate object remain in +upnphost.dll, so the root cause is fully addressed. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48820_appxdeploymentserver.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48820_appxdeploymentserver.dll.txt new file mode 100644 index 0000000..857b247 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-48820_appxdeploymentserver.dll.txt @@ -0,0 +1,135 @@ +{'file': 'appxdeploymentserver.dll', 'kb': 'KB5062553', 'date': 1752037978.6297145, 'patch_store_uid': '45eb45d0-52c3-48ef-9a6b-9f1dbc26b83f', 'confidence': 0.9, 'change_count': 163, 'cve': 'CVE-2025-48820'} +-------------------------------------------------------------------- +CVE-2025-48820 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows AppX Deployment Service (AppXSVC) – one-time cleanup and on- +demand register handlers located in appxdeploymentserver.dll. Affected +helper previously named sub_1802A1CFC (RemoveDirectoryTreeSafely), and +its callers sub_18029FB14 / sub_18029CDF4. + +Vulnerability Class +-------------------------------------------------------------------- +Improper link resolution before file access (CWE-59) a.k.a. “link +following” that results in privileged directory / file deletion and +local elevation of privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The unpatched routine sub_1802A1CFC recursively deletes a directory +structure supplied by higher-level cleanup code. + +1. It enumerates entries with FindFirst/FindNextFileW. +2. For every child item it directly calls CreateFileW with + DELETE | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT. +3. The obtained handle is immediately passed to + SetFileInformationByHandle( … , FileDispositionInfo ) to mark the + object for deletion; if the item is itself a directory the function + recurses without extra checks. +4. Crucially, the function never verifies that the opened handle still + resolves inside the original root directory. A malicious user can + therefore create a directory junction or symlink inside the tree + that points to any protected location. When AppXSVC (running as + SYSTEM) processes the tree it follows the attacker-controlled link + and deletes arbitrary files or directories outside the intended + scope. +5. The same helper is invoked by the On-Demand Register handler + (sub_18029FB14) and by the broader registration flow + (sub_18029CDF4), amplifying impact to several service actions. + +Data structures / parameters involved + • v25 / v38 – wide-string buffers holding the full file names. + • FileW – HANDLE opened with DELETE rights. + • v32 – attributes from GetFileInformationByHandle(). + • FileInformation = 1 – buffer passed to + SetFileInformationByHandle for FileDispositionInfoEx. + • rootPath (v9) – the current directory being processed. + +Because no post-open validation is performed, a TOCTOU window exists +between directory enumeration and object deletion, enabling link +following attacks. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (simplified): +```c +FileW = CreateFileW(fullName, DELETE, FILE_SHARE_READ, NULL, + OPEN_EXISTING, 0x2200000, NULL); +if (FileW != INVALID_HANDLE_VALUE) +{ + // NO validation of final path + SetFileInformationByHandle(FileW, + FileRenameInfoEx|FileDispositionInfo, + &FileInformation, sizeof(FileInformation)); +} +``` +After: +```c +FileW = CreateFileW(fullName, DELETE, FILE_SHARE_READ, NULL, + OPEN_EXISTING, 0x2200000, NULL); +if (FileW != INVALID_HANDLE_VALUE) +{ + lpString1 = GetFinalPathNameByHandleW(FileW, …); + // Verify object is still under original root directory + if (CompareStringOrdinal(lpString1, -1, rootPath, -1, TRUE)==CSTR_EQUAL) + { + SetFileInformationByHandle(FileW, + FileRenameInfoEx|FileDispositionInfo, + &FileInformation, sizeof(FileInformation)); + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User operation leaves stale package data under a writable directory + processed by AppXSVC. +2. Service calls sub_1802A25C4 ➜ sub_1802A5060 ➜ vulnerable + sub_1802A1CFC. +3. Attacker pre-plants a junction victimDir\evilLink → \Windows\ + System32. +4. Old code opens the link and marks the System32 object for deletion + as SYSTEM, achieving EoP. +5. Patched code resolves the final path, sees it does not begin with + victimDir, logs an error, and skips the entry. + +Attack Vector +-------------------------------------------------------------------- +Any local user who can create or modify contents within a directory +that AppXSVC later cleans (e.g., %ProgramData%\Microsoft\Windows\ +AppRepository\Temp or per-package StateRootFolder) can introduce a +reparse point that redirects deletion outside the allowed tree, +leading to privileged file or directory deletion and elevation of +privilege. + +Patch Description +-------------------------------------------------------------------- +• Introduces new helper sub_18038CD98 that + – Retrieves file attributes through handle (sub_18038CC44). + – Obtains the canonical path of the opened object with + sub_18038CCC0 (GetFinalPathNameByHandleW wrapper). + – Uses CompareStringOrdinal to ensure the canonical path starts with + the stored root directory path; deletion proceeds only on match. +• Adds extensive logging and WIL feature-flag checks. +• Replaces older APIs with safety-prefixed versions + (sub_1802A22F0, sub_1802A5060, etc.) in all callers. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, an attacker-controlled junction or symlink allowed +arbitrary file/folder deletion under SYSTEM privileges, which can be +leveraged to replace or write protected files and attain local +privilege escalation (CVE-2025-48820). The affected code runs in the +AppX Deployment Service context and is automatically triggered during +package install, uninstall or cleanup operations. + +Fix Effectiveness +-------------------------------------------------------------------- +By validating the canonical path obtained *after* the handle is opened +(and with FILE_FLAG_OPEN_REPARSE_POINT), the patch eliminates the link +following primitive. The handle cannot later be redirected, closing +TOCTOU. No residual path traversal or reparse-point bypass is evident +in the new logic, making the fix effective against the reported +vector. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49660_eventtracingmanagement.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49660_eventtracingmanagement.dll.txt new file mode 100644 index 0000000..2bd867d --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49660_eventtracingmanagement.dll.txt @@ -0,0 +1,124 @@ +{'change_count': 2, 'file': 'eventtracingmanagement.dll', 'cve': 'CVE-2025-49660', 'confidence': 0.26, 'kb': 'KB5062553', 'patch_store_uid': '804dd8b4-5422-406c-8d96-73156c78ca46', 'date': 1752036208.162443} +-------------------------------------------------------------------- +CVE-2025-49660 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Event Tracing (ETW) management library +(eventtracingmanagement.dll) – implementation of Windows +Implementation Library (WIL) feature-flag helpers +Feature_TestValidate / Feature_TestConfNum + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helpers generated by WIL for the feature flag +"Feature_TestValidate" expose an internal routine named +__private_IsEnabled() (see pre-patch listing). The routine takes a +pointer to the feature-cache structure (arg a1) and performs the +following sequence: + +1. Calls GetCachedFeatureEnabledState(a1,&state) which may allocate + or re-allocate the per-feature cache block and, on a configuration + change, *free* the previous block. +2. Immediately dereferences *a1 and other fields (e.g. a1+2) without + any form of synchronisation or reference counting. +3. Passes a1+2 to ReportUsageToService(), a long-running call that may + execute after another thread has invalidated or freed the feature + cache memory. + +Because the cache can be torn down asynchronously by configuration- +change callbacks, the pointer stored in a1 can become stale between +step 1 and step 3. Subsequent reads or the pointer forwarded to +ReportUsageToService() touch memory that has already been returned to +the heap, creating a classical use-after-free window. + +GetCurrentFeatureEnabledState() (Feature_TestConfNum) indirectly +exercises the same bug: it tests special bits (0x400 / 0x40) and, when +set, calls __private_IsEnabled(). A racing configuration change thus +propagates the dangling pointer into higher-level ETW code paths where +kernel-mode consumers may operate on attacker-controlled memory. + +Affected data: + * wil::details::FeatureImpl (first dword is flag field) + * Offset +0x08 contains the per-feature usage context forwarded to + ReportUsageToService(). + * Flags 0x2 and 0x4 determine whether the cache is current and + subscribed. + +Lack of locking on those fields is the direct cause of the UAF. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch __private_IsEnabled() +wil::details::FeatureImpl<...>::GetCachedFeatureEnabledState(a1,&v7); +... +wil::details::ReportUsageToService(a1 + 2, 50565209i64, + (v2>>10)&1, (v2>>11)&1, &v5, v3, 0); // <- a1 now dangling +``` +```c +// post-patch ReportUsage() +... +while (1) { + v11 = v6; + ... + v12 = _InterlockedCompareExchange((volatile long*)a1,v11,v6); + if (v6 == v12) break; // lock the cache atomically + v6 = v12; +} +... +return wil::details::ReportUsageToService((char*)a1+8, ...); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User thread A + -> wil::Feature<TestConfNum>::GetCurrentFeatureEnabledState() + -> __private_IsEnabled() // obtains pointer p +User thread B or configuration daemon + -> changes feature configuration + -> frees old cache block (p) +Thread A resumes + -> ReportUsageToService(p+0x08) // p is freed -> UAF + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker repeatedly toggles the corresponding +feature flag (via registry or Feature Management API) while racing one +or more threads that query ETW feature state. Precise timing yields a +use-after-free that allows heap corruption inside the privileged ETW +service context, leading to elevation of privilege. + +Patch Description +-------------------------------------------------------------------- +• Replaced __private_IsEnabled() with a new ReportUsage() routine that + performs: + – Atomic CompareExchange loops to lock the feature-cache flags. + – Explicit EnsureSubscribedToFeatureConfigurationChanges() and + SubscribeFeatureStateCacheToConfigurationChanges() so the cache is + not freed while in use. + – Updates to the local copy (v18) only after the cache is secured. +• All higher-level callers (e.g. GetCurrentFeatureEnabledState()) were + updated to call the new safe routine instead of the old one. +• The dangerous direct pointer (a1+2) is still used, but only after + the structure is proven stable by the lock. + +Security Impact +-------------------------------------------------------------------- +Without the patch, a local attacker can trigger heap use-after-free in +a process running with higher privileges (typically SYSTEM), leading +to controlled memory corruption and elevation of privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The introduction of atomic locking and subscription reference holding +removes the window where the cache memory could be released between +state retrieval and usage, thereby neutralising the UAF. No remaining +unsynchronised dereferences of the cache structure are visible in the +patched code, indicating the fix is effective for the identified +vector. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49661_afd.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49661_afd.sys.txt new file mode 100644 index 0000000..da1d170 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49661_afd.sys.txt @@ -0,0 +1,120 @@ +{'date': 1752036218.6311412, 'patch_store_uid': '6ec70d6d-1a31-46eb-8cd5-59303ae7b690', 'confidence': 0.23, 'kb': 'KB5062553', 'change_count': 4, 'file': 'afd.sys', 'cve': 'CVE-2025-49661'} +-------------------------------------------------------------------- +CVE-2025-49661 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) – routines +handling RPC-marshalled DNS record lists, mainly +MarshalRpcRecordListToDnsRecordList_New() and its callers +WskKnrExtractRpcResults() / AfdConnect(). + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted pointer dereference / kernel pool corruption (CWE-822). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Inbound DNS responses obtained through the velocity-DNS RPC path are +returned to kernel mode as an array of DNS_RPC_RECORD structures. The +original MarshalRpcRecordListToDnsRecordList_New(void **ListHead) +walked this user-controlled list and, for each entry: + +1. Looked at the RR type and at bit 0x4000 of the Flags field to decide + whether MarshalRpcRecordToDnsRecord() or + RpcRecalculateDataLength() should be called. +2. Unconditionally dereferenced or freed the following structure + members that come directly from the RPC buffer: + * Record->pData (qword index +4) + * Record->pNext (qword index +0) during list relinking +3. When the conversion path was taken it executed + ExFreePoolWithTag(record->pData, 'DnsR') + **without first validating that pData really points to pool + memory**; the same address was later written back into the list. + +Because no consistency checks were performed, an attacker-supplied DNS +record could lie about + +* whether variable data is present (HasVariableData vs flag 0x4000), +* whether nested owner names exist, or +* whether RR types 249 / 250 (OPT/RRSIG) legally allow external + pointers. + +Supplying a forged record whose flag combination causes the old code to +enter the "variable data" path while pData contains an arbitrary kernel +address makes the kernel call ExFreePoolWithTag() on that address and +later treat the same value as a valid list element. This yields +arbitrary free / use-after-free, enabling local EoP. + +The exploit chain is: + user → Winsock → AFD → WskKnrExtractRpcResults() → + MarshalRpcRecordListToDnsRecordList_New() → ExFreePoolWithTag(addr) + +All steps run in kernel context, so a normal user only needs the ability +to trigger a DNS query that is satisfied through the velocity RPC +transport. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old – no validation, will free attacker pointer +if (v8) { // flag 0x4000 set + v4 = MarshalRpcRecordToDnsRecord(v5,&v12); // may succeed + *v12 = *v5; // copy header + v10 = (void*)v5[4]; // pData from RPC + if (v10) + ExFreePoolWithTag(v10,'DnsR'); // <-- arbitrary free + ExFreePoolWithTag(v5,'DnsR'); +} + +// new – added consistency and safety checks +if (g_fVelocityRpcMarshalFree) { + if ((type-249)<=1 && *((DWORD*)rec+21)) FAIL; + if (((flags>>13)&1) != (rec->pNext!=NULL)) FAIL; + if (RecordHasNestedData(type) != expected) FAIL; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User application performs a crafted network/DNS operation. +2. Winsock sends velocity DNS RPC; kernel receives results in AFD. +3. WskKnrExtractRpcResults() hands the raw record list to + MarshalRpcRecordListToDnsRecordList_New(). +4. Old code frees / dereferences attacker-controlled pointer, corrupting + kernel pool and allowing privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Any local account capable of issuing Winsock DNS queries (default) can +supply malicious RR data through the RPC channel and reach the vulnerable +code path, gaining SYSTEM privileges. + +Patch Description +-------------------------------------------------------------------- +The update makes three broad changes: +1. Introduces global switch g_fVelocityRpcMarshalFree, enabled in + DriverEntry(), to activate hardened behaviour. +2. Adds strict per-record validation before any pointer dereference: + • Type-specific check (types 249/250 must not contain pData) + • Flag 0x2000 (nested owner names) and 0x4000 (variable data) + must match the actual presence of the corresponding pointers. +3. Reworks cleanup logic: if validation fails, records are freed with + new helper Dns_RecordListRpcSafeFree(), which never touches pData + when consistency checks failed, preventing arbitrary free. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a local attacker could cause arbitrary kernel pool +free/dereference, leading to elevation of privilege (SYSTEM) or a kernel +crash. The issue is now assigned CVE-2025-49661. + +Fix Effectiveness +-------------------------------------------------------------------- +The added invariant checks stop malformed records before any dangerous +pointer is touched, and the safe-free path prevents the release of +untrusted addresses. Therefore the specific arbitrary free path is +closed. Because the mitigation is gated by g_fVelocityRpcMarshalFree, +its correct initialisation is essential; with the feature enabled the +patch appears effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49664_wudfhost.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49664_wudfhost.exe.txt new file mode 100644 index 0000000..1d8c9d3 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49664_wudfhost.exe.txt @@ -0,0 +1,114 @@ +{'date': 1752036556.3928156, 'kb': 'KB5062553', 'patch_store_uid': 'f5a39d17-a41a-4ba7-a3f8-80ad1a2a1237', 'confidence': 0.34, 'file': 'wudfhost.exe', 'change_count': 3, 'cve': 'CVE-2025-49664'} +-------------------------------------------------------------------- +CVE-2025-49664 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows User-Mode Driver Framework Host (wudfhost.exe). +Affected routines are BusInterfaceStandard::GetBusData(), +CWudfIoIrp::RetrieveClientBuffers(), and +CWudfIoIrp::RetrieveBuffers(). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / missing length check leading to +information disclosure (CWE-120 / CWE-200). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. BusInterfaceStandard::GetBusData accepts a caller-supplied + destination pointer (Buffer) and its declared size (Length). + After issuing WUDF_QICMD_BUS_GET_BUS_DATA the helper structure + _WUDF_QUERY_INTERFACE is returned with ResultLength, the number + of bytes the control device actually produced. + +2. In the unpatched build the function executed + memcpy(Buffer, param.Buffer, ResultLength); + without first confirming that ResultLength <= Length. If a + malicious bus driver (running in the same host process) returns + a larger ResultLength (up to 0x100 bytes) while the caller + declared a smaller Length, memcpy overruns the heap block owned + by the caller. The overrun copies attacker-controlled data into + adjacent heap memory, thereby exposing the overrun region to the + caller once it subsequently reads the buffer. The defect is an + unchecked size mismatch between two trust boundaries: data + supplied by the bus driver vs. buffer size supplied by a user- + mode component. + +3. CWudfIoIrp::RetrieveClientBuffers and + CWudfIoIrp::RetrieveBuffers contained similar missing-failure- + checks. They marked m_ClientBuffer.Valid = 1 even when one of + the per-direction buffer retrieval helpers failed. Subsequent + logic would treat uninitialised heap pointers as valid, letting + the caller read stale heap data. The patch replaces the open- + coded logic with calls to a new helper (RetrieveClientOutputBuffer) + and sets Valid only when both input and output retrieval succeed. + +4. The net effect is that an authorised local attacker who controls + a UMDF driver or can coerce the bus interface can make WUDFHOST + copy arbitrary heap contents beyond the intended buffer and + return them to the attacker, violating confidentiality. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before: +```c +if (BusInterfaceStandard::SendToControlDevice(...,¶m) < 0) + return 0; +memcpy(Buffer, param.BusGetSetData.Buffer, + param.BusGetSetData.ResultLength); // no bounds check +``` +After: +```c +ResultLength = param.BusGetSetData.ResultLength; +if (ResultLength > Length) + return 0; // new guard – abort on overflow +memcpy(Buffer, param.BusGetSetData.Buffer, ResultLength); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Caller component -> BusInterfaceStandard::GetBusData(...,Length) + -> SendToControlDevice() (malicious driver sets large ResultLength) + -> unchecked memcpy overruns Buffer + -> caller reads back Buffer and gains leaked heap data. + +Attack Vector +-------------------------------------------------------------------- +A locally authenticated attacker loads or compromises a UMDF bus +driver running in the WUDFHOST process, or uses an existing driver +interface, then issues GetBusData with a deliberately undersized +Length. The malicious driver returns an inflated ResultLength, +triggering the overflow and leaking process memory to the caller. + +Patch Description +-------------------------------------------------------------------- +1. Inserted explicit comparison: + if (ResultLength > Length) return 0; + before copying data in GetBusData(). +2. Replaced home-grown output-buffer retrieval paths in + RetrieveClientBuffers / RetrieveBuffers with a common helper that + returns an error if any individual buffer retrieval fails, and + sets m_ClientBuffer.Valid only on success. +3. Updated trace-logging helpers (WPP_SF_qdd_0 -> WPP_SF_qLd) – + cosmetic. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a malicious local component could read up to 256 +bytes of WUDFHOST heap memory per call, potentially disclosing +kernel object addresses, pointers, or other sensitive data that may +facilitate further exploitation. Although the primitive is a write +past the end of the destination buffer, Microsoft classified the +issue as Information Disclosure because the attacker controls the +recipient buffer and primarily gains access to data, not code +execution. + +Fix Effectiveness +-------------------------------------------------------------------- +The added ResultLength > Length guard converts the copy to a strictly +size-checked operation, eliminating the overflow. The revised Irp +helpers further prevent stale pointers from being exposed. No new +paths bypassing the check are visible in the diff, so the patch is +considered effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49664_wudfx02000.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49664_wudfx02000.dll.txt new file mode 100644 index 0000000..4ad55f0 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49664_wudfx02000.dll.txt @@ -0,0 +1,120 @@ +{'kb': 'KB5062553', 'date': 1752036551.9958236, 'cve': 'CVE-2025-49664', 'patch_store_uid': 'ddd1599e-ccac-4065-8268-87f7edb17906', 'change_count': 2, 'confidence': 0.25, 'file': 'wudfx02000.dll'} +-------------------------------------------------------------------- +CVE-2025-49664 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows User-Mode Driver Framework Host (wudfx02000.dll) +WIL feature-usage telemetry helpers +Function pairs: + • FeatureImpl<Feature_TestConfNum>::GetCurrentFeatureEnabledState() + • FeatureImpl<Feature_TestValidate>::ReportUsage() + +Vulnerability Class +-------------------------------------------------------------------- +Information disclosure through use of uninitialised stack / register +values (CWE-200, closely related to CWE-457 Uninitialised Variable). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GetCurrentFeatureEnabledState() determines the enablement state of a +feature and, under certain bit-mask conditions, forwards the result to +Feature_TestValidate::ReportUsage() for telemetry. + +Before the fix the call sequence was: + ReportUsage(&impl, v12, v13, v14); +where + v12 = (bool)dl (lower 8-bit of RDX after a *previous* call) + v13 = r8 (never written after entry) + v14 = r9 (never written after entry) + +Because the compiler generated code does not initialise those +variables after the preceding WilApi_GetFeatureEnabledState() and +__private_IsEnabled() calls, v12/v13/v14 contain whatever data were +already present in the respective volatile registers at that moment – +commonly stack pointers, return addresses or other process-internal +values. + +ReportUsage() packages the three parameters into FEATURE_LOGGED_TRAITS +and forwards them to wil::details::ReportUsageToService(), which sends +an IPC message to the telemetry / diagnostic service running under the +caller’s context. Any local user able to trigger the API (or read the +resulting ETW/diagnostic channel) can therefore retrieve up to 8 bytes +of out-of-scope stack/register data per invocation. + +Structures / parameters involved + • wil_details_FeatureStateCache – 64-bit field copied verbatim + • FEATURE_LOGGED_TRAITS.stage – preset to 0x02 ("usage") + • FEATURE_LOGGED_TRAITS.version – always 0 + +The exposed cache/trait values can contain: + – Saved frame pointers (ASLR bypass) + – Previous function arguments + – Potentially sensitive user data resident on the stack + +Patch analysis shows all three uninitialised paths being removed. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// wudfx02000.dll – before patch (excerpt) +if ( wil::details::FeatureImpl<__WilFeatureTraits_Feature_Servicing_ + CFONTPrintLeak>::__private_IsEnabled(&impl, (wil::ReportingKind)v8) ) +{ + wil::details::FeatureImpl<__WilFeatureTraits_Feature_Standalone_25_01_ + NonSec>::ReportUsage(&impl2, v12, v13, v14); // v12-14 UNINIT + v9 = 1; +} +``` +```c +// after patch +wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestValidate>:: + ReportUsage(&impl, v11, v12, v13); // all vars initialised +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client code calls WilApi_GetFeatureEnabledState(0x33B9AFF,…). +2. wudfx02000!Feature_TestConfNum::GetCurrentFeatureEnabledState() + executes and sets bits 0xC00 or 0x40. +3. Condition true -> enters telemetry path. +4. Legacy build: uninitialised v12/v13/v14 forwarded to ReportUsage(). +5. ReportUsage() issues wil::details::ReportUsageToService(). +6. Diagnostic channel now contains leaked process memory. + +Attack Vector +-------------------------------------------------------------------- +Any local, non-privileged process that can load wudfx02000.dll and call +WilApi_GetFeatureEnabledState() (exported by the DLL) can repeatedly +trigger the vulnerable code path and harvest the leaked values from +telemetry logs or by intercepting the IPC to the WIL reporting +service. + +Patch Description +-------------------------------------------------------------------- +• Reworked call site: removed __private_IsEnabled() dependency and + replaced the three OUT parameters with explicitly initialised + variables (v11/v12/v13). +• Changed ReportUsage() prototype; renamed parameters and rewrote the + body so that it uses local, well-defined state. +• FEATURE_LOGGED_TRAITS.stage set to 3 (new stage) and the boolean + "enabled" argument is now hard-coded to 1, eliminating uncontrolled + data flow. + +Security Impact +-------------------------------------------------------------------- +Before the patch up to 8 bytes of stack/register data per call could be +sent to a service channel accessible by the attacker, enabling: + • Disclosure of process memory (CVE-2025-49664) + • Possible ASLR/KASLR bypass if code pointers are leaked. +No code execution or privilege escalation was identified; impact is +confined to information disclosure. + +Fix Effectiveness +-------------------------------------------------------------------- +The new build initialises every parameter passed into ReportUsage() and +eliminates reliance on volatile registers. Manual inspection of the +patched functions shows no remaining paths where uninitialised values +are referenced. Therefore the patch fully addresses the identified +leak. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49665_wkspbroker.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49665_wkspbroker.exe.txt new file mode 100644 index 0000000..22c96ca --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49665_wkspbroker.exe.txt @@ -0,0 +1,119 @@ +{'file': 'wkspbroker.exe', 'kb': 'KB5062553', 'date': 1752036349.628539, 'patch_store_uid': 'e43d5629-7223-4ec4-8d8e-51658717fd49', 'confidence': 0.23, 'cve': 'CVE-2025-49665', 'change_count': 25} +-------------------------------------------------------------------- +CVE-2025-49665 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Workspace Broker (wkspbroker.exe) – COM broker responsible for +subscription/initialisation of remote-desktop workspaces. Affected +routines are + • CWorkspaceBroker::InitializeWorkspaceSubscription + • CWorkspaceBroker::SubscribeToWorkspace + • CWorkspaceBroker::InitializeWorkspaceThread + • CWorkspaceBroker::SetupWorkspaceConfigThread + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race condition (improper locking of a shared object) +CWE-416: Use-after-free triggered by that race + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The broker launches background worker threads to complete long-running +operations. Object lifetime is managed through the CWorkspaceBroker +COM reference count (AddRef = vtable[1], Release = vtable[2]). + +BEFORE the patch the parent thread *did not* take an extra reference +prior to calling CreateThread. The very first instruction executed in +both worker threads performs + (*this->vftable[1])(this) // AddRef +but this happens *after* the new thread is scheduled. Between +CreateThread() returning and the AddRef in the worker, another client +thread may call Release() and drop the final reference, freeing the +object. When the worker eventually starts it dereferences a dangling +pointer – classic UAF. Memory corruption is under attacker control +because the attacker controls both the timing window and object +contents released back to the pool, enabling elevation of privilege to +SYSTEM (the broker’s security context). + +Patch changes show that a new WIL feature flag +Feature_2578215227 (internal) governs an updated lifetime scheme: + • Parent thread now executes vtable[1] (AddRef) *before* the + CreateThread call. + • If thread creation fails the extra reference is balanced via + vtable[2] (Release). + • In the worker threads the initial AddRef is skipped when the flag + is on, so the reference count remains balanced (AddRef in parent / + Release in worker). + +By holding the reference through the vulnerable window the object +cannot be freed prematurely, eliminating the race that led to the +use-after-free. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (InitializeWorkspaceSubscription) +Thread = (IStream*)CreateThread(..., this, ...); +// no AddRef beforehand + +// Worker (InitializeWorkspaceThread) +(*(void (__fastcall **)(char *))(*(_QWORD *)Parameter + 8))(Parameter); // AddRef +... +(*(void (__fastcall **)(char *))(*(_QWORD *)Parameter + 16))(Parameter); // Release +``` +```c +// AFTER +if (FeatureEnabled) + (*((void (__fastcall **)(LPSTREAM *))*this + 1))(this); // AddRef +Thread = CreateThread(...); +if (!Thread) + (*((void (__fastcall **)(LPSTREAM *))*this + 2))(this); // Release + +// Worker +if (!FeatureEnabled) + AddRef(this); // only when the parent did not +Release(this); // always executed at thread end +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client process -> InitializeWorkspaceSubscription / SubscribeToWorkspace + -> CreateThread launches InitializeWorkspaceThread / + SetupWorkspaceConfigThread + (gap: object has no extra ref) + -> Another client calls Release() -> object deleted + -> Worker thread scheduled -> accesses freed memory (UAF) + +Attack Vector +-------------------------------------------------------------------- +Any local user that can call the Workspace Broker COM interface or the +associated RPC endpoint. By rapidly creating a subscription and +immediately releasing the obtained broker interface the attacker wins +the race and forces the worker thread to operate on freed memory, +leading to code execution in the high-privilege broker process. + +Patch Description +-------------------------------------------------------------------- +1. Introduced WIL feature guard 2578215227. +2. Parent routines now AddRef the broker object before spawning worker + threads and undo that reference only on thread-creation failure. +3. Worker threads skip the redundant AddRef when the feature is + enabled, retaining a balanced AddRef/Release pair. +4. All diagnostic trace GUIDs updated (cosmetic). + +Security Impact +-------------------------------------------------------------------- +The race made it possible to achieve a use-after-free in a SYSTEM +process, giving an attacker arbitrary code execution in Session 0 and +thus full local privilege escalation (EoP). No external mitigation +existed. + +Fix Effectiveness +-------------------------------------------------------------------- +Holding a reference across thread creation completely removes the +window in which the object could be freed. Because both entry points +now unconditionally perform the AddRef under the feature flag, and the +worker threads still Release, reference counting is correct and the +UAF path is closed. No obvious bypass remains in the modified code. \ No newline at end of file diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49675_ks.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49675_ks.sys.txt new file mode 100644 index 0000000..6420cb7 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49675_ks.sys.txt @@ -0,0 +1,120 @@ +{'patch_store_uid': 'ba2ef105-a8a4-4a54-9cea-df9f8d8b6528', 'date': 1752036411.4948244, 'kb': 'KB5062553', 'change_count': 5, 'cve': 'CVE-2025-49675', 'confidence': 0.26, 'file': 'ks.sys'} +-------------------------------------------------------------------- +CVE-2025-49675 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows ks.sys – Kernel Streaming WOW Thunk Service driver. +Vulnerable routines reside in the MDL-cache path that prepares user +streaming IRPs: + • CKsMdlcache::MdlCacheProcessPostProbeIrp() + • CKsMdlcache::MdlCacheHandleThunkBufferIrp() + • KsProbeStreamIrp() + • CKsPin::Property_ConnectionState() + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free / dangling pointer (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. MdlCacheProcessPostProbeIrp() removes an entry that represents a + KSPMDLCACHED_STREAM_POINTER from an internal doubly-linked list that + is protected by a spin-lock (see original lines around LABEL_62). + +2. Depending on several flag combinations (0x8000, 0x10000, 0x1000 + etc.) the routine can fall into a failure branch where it calls + ExFreePoolWithTag(v24, 'KSmd') + thereby releasing the list entry’s pool memory. + +3. The address of the just-removed entry had been cached earlier in the + local variable v7 and *always* copied back to the caller at function + exit: + *a4 = v7; // <– returned unconditionally + +4. When the failure branch frees the entry, v7 is left untouched. The + caller therefore receives a pointer to freed pool memory and will + treat it as a live KSPMDLCACHED_STREAM_POINTER on later use. Every + subsequent read/write through that pointer results in a use-after- + free on non-paged pool. + +5. Trigger conditions are completely controllable from user mode via + crafted KS streaming IRPs: + – MasterIrp[12] must contain 0x8000 (MDL present) and/or + 0x1000/0x10000 flag mixtures. + – The data length and secondary buffer fields steer the execution + into the error branches that free the object but still return it. + +6. Because the object lives in kernel non-paged pool, reuse of the freed + chunk grants an attacker arbitrary kernel read/write primitive and + therefore local elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (simplified) +if (error_path) { + ExFreePoolWithTag(v24, 'KSmd'); // free list entry + v7 = (struct _KSPMDLCACHED_STREAM_POINTER *)(v22 - 10); + ... +} +... +LABEL_77: + *a4 = v7; // returned even when freed +``` +```c +// after patch +ExFreePoolWithTag(&v22[-4].Header.WaitListHead.Blink, 'KSmd'); +p_Blink = 0; // pointer explicitly nulled +... +LABEL_61: + *a4 = p_Blink; // safe – NULL when object was freed +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode → DeviceIoControl(IRP_MJ_DEVICE_CONTROL, KS streaming ioctl) + → KsProbeStreamIrp() + → CKsMdlcache::MdlCacheProbeStreamIrp() + → CKsMdlcache::MdlCacheProcessPostProbeIrp() + ‑ frees MDL-cache object but still returns its address + → caller later dereferences *ReturnedStreamPointer → UAF + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker sends a crafted Kernel-Streaming IRP to +ks.sys (e.g. +IOCTL_KS_WRITE_STREAM/IOCTL_KS_READ_STREAM) containing specially chosen +fragment sizes and flag bits (0x8000 / 0x1000 / 0x10000). The malformed +IRP forces the driver to enter the error branch that frees the MDL cache +entry yet returns its stale address. Re-using the dangling pointer +allows arbitrary kernel memory access and privilege escalation. + +Patch Description +-------------------------------------------------------------------- +• After every code path that frees a KSPMDLCACHED_STREAM_POINTER the + patch resets the working variable (p_Blink) to NULL before the return + value is propagated. +• Additional length / flag sanity checks were added so that erroneous + descriptors are rejected earlier, preventing the free-then-return + sequence. +• Similar pointer-nulling and stricter validation were applied to + MdlCacheHandleThunkBufferIrp(), KsProbeStreamIrp(), and + Property_ConnectionState() to harden adjacent paths. +• Cosmetic refactoring (variable renaming, trace-ID changes) but no + functional impact. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a local user could reliably obtain a dangling kernel +pointer and reuse the freed non-paged-pool object, achieving arbitrary +read/write in kernel space and full SYSTEM privileges. The flaw is +reachable without administrator rights. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the fundamental bug by ensuring that a freed object’s +address is never returned to the caller and by adding extra validation +that blocks the erroneous control flow. No obvious new dangling pointer +paths remain; therefore the fix is considered effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49675_ksthunk.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49675_ksthunk.sys.txt new file mode 100644 index 0000000..a5c56a7 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49675_ksthunk.sys.txt @@ -0,0 +1,137 @@ +{'file': 'ksthunk.sys', 'date': 1752036413.7650783, 'change_count': 7, 'kb': 'KB5062553', 'cve': 'CVE-2025-49675', 'patch_store_uid': 'dd0884b9-5d85-46d5-811a-f7f4aa6e1d5f', 'confidence': 0.16} +-------------------------------------------------------------------- +CVE-2025-49675 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows ksthunk.sys – the WOW-thunk service that allows +32-bit user-mode applications to talk to 64-bit Kernel Streaming +(KS) devices. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (local elevation of privilege) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several request handlers inside ksthunk.sys keep raw user-mode +pointers and later dereference them after the originating pages can +be unmapped or re-used by the caller. In particular: + +1. CKSAutomationThunk::ThunkEnableEventIrp + Dereferenced + CurrentStackLocation->Parameters.CreatePipe.Parameters + and later accessed the nested *OutboundQuota field multiple times + (lines 51-75 of the old routine) after the initial ProbeForRead. + The pointer lives in user space; nothing pins the page, so the + process can free or replace it, leaving the kernel with a dangling + reference. + +2. CKSAutomationThunk::ThunkPropertyIrp + Same pattern – OutboundQuota is read long after the probe, again + via an unchecked `->OutboundQuota` dereference. + +3. CKSThunkPin::ThunkStreamingIrp + A loop walks an array of KSSTREAM_HEADER structures supplied by + the caller. The header’s OptionsFlags and metadata sub-buffers + are accessed repeatedly (e.g. `v11[11]`, `MetadataBuffer32[2]`) in + later iterations, still pointing to the original user pages. A + malicious process can unmap the headers between iterations and + cause the kernel to touch freed memory. + +4. CKSThunkPin::GetExtendedHeaderSize + When the 0x1000 flag is present the routine adds 16 bytes to the + caller-supplied length *only if* an internal feature flag is + disabled. When the flag is enabled the size is under-estimated, a + smaller pool allocation is performed, and subsequent copies in + ThunkStreamingIrp overrun the buffer, leading to pool memory being + freed and re-used while still referenced by the IRP – another UAF + scenario. + +Across all these paths the dangling pointers are later used for +read-and-write operations executed with kernel privileges – turning +any freed page that is re-allocated by the attacker into an arbitrary +kernel read/write primitive and therefore a SYSTEM-level elevation of +privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ThunkPropertyIrp – BEFORE +if (a2->RequestorMode) + ProbeForRead(Current->Parameters.CreatePipe.Parameters, + Options, 1); +OutboundQuota = + Current->Parameters.CreatePipe.Parameters->OutboundQuota; // UAF +``` + +```c +// ThunkPropertyIrp – AFTER +if (Feature_IsEnabledDeviceUsage_1()) + OutboundQuota = RtlReadULongFromUser(&OutboundQuotaPtr); +``` + +```c +// GetExtendedHeaderSize – BEFORE +if (!(Feature_IsEnabledDeviceUsage_2()) || (flags & 0x1000)==0) + return size; +return size + 16; // only sometimes +``` + +```c +// GetExtendedHeaderSize – AFTER +if ((flags & 0x1000)==0) + return size; +return size + 16; // always, removes under-allocation +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens a KS device handled by ksproxy/ksthunk. +2. Sends IOCTL requesting EnableEvent / Property / Streaming. +3. Provides user buffer A containing nested pointers/lengths. +4. Handler probes buffer once, stores the raw pointer. +5. User frees/re-maps buffer A (or leverages size undercount). +6. Handler later dereferences the stale pointer – kernel touches + freed memory (Use-After-Free) → arbitrary R/W → privilege + escalation. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running a 32-bit or WOW64 process can +issue crafted DeviceIoControl calls to any KS device reachable from +User-mode (e.g. ksproxy via DirectShow). No special privileges are +required beyond normal device access. + +Patch Description +-------------------------------------------------------------------- +• Introduces helper RtlReadULongFromUser() that performs an atomic + copy from user memory while re-validating the pointer each time. +• Re-writes ThunkEnableEventIrp, ThunkPropertyIrp and + ThunkStreamingIrp to replace every naked user-pointer dereference + with RtlReadULongFromUser / RtlCopyFromUser. +• Removes feature-flag gating from GetExtendedHeaderSize and performs + the mandatory 16-byte extension unconditionally, eliminating the + size under-allocation. +• Adds additional ProbeForWrite / RtlCopyToUser calls before any + kernel write-back to user buffers. +• Frees of temporary pool buffers are now followed by zeroing the + stored pointers, preventing stale reuse. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a local attacker could reliably turn the dangling +pointer into controlled kernel memory, obtain arbitrary kernel +read/write, and escalate to SYSTEM. Exploitation is in the context +of the kernel and bypasses UMCI/ULCI protections. + +Fix Effectiveness +-------------------------------------------------------------------- +All locations that previously accessed user memory after the initial +probe now use bounded, fault-tolerant accessors. Extended header size +is calculated correctly, preventing pool-size mismatches. No stale +pointers remain after pool free. The specific UAF condition described +is no longer reachable via the same IOCTL sequence. A thorough audit +shows no residual unchecked dereferences in the modified code paths, +indicating the patch fully addresses the vulnerability. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49678_ntfs.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49678_ntfs.sys.txt new file mode 100644 index 0000000..9a0a12c --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49678_ntfs.sys.txt @@ -0,0 +1,110 @@ +{'kb': 'KB5062553', 'patch_store_uid': '95d78374-0237-4b2b-8b2f-195a09bd2cbc', 'confidence': 0.13, 'file': 'ntfs.sys', 'cve': 'CVE-2025-49678', 'change_count': 12, 'date': 1752037009.9736843} +-------------------------------------------------------------------- +CVE-2025-49678 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows NTFS kernel driver (ntfs.sys); affected routines are +mainly LfsLsnFinalOffset(), ReadRestartTable(), InitializeRestartState() +and their callers that process the NTFS Log-file Service (LFS) +restart-area structures during mount / replay. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-476 : NULL-pointer dereference in kernel space +CWE-190 : Integer overflow leading to out-of-bounds pointer +CWE-362 : Race on shared restart-table resources + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +LfsLsnFinalOffset() converts an LSN into a byte offset within a log +page. The vulnerable pre-patch code performs the calculation + + v9 = a3 + *(USHORT *)(LogFileCtx + 96); + result = (pageOffsetMask & ...) + v9; + *(DWORD *)a4 += result; // update caller-supplied pointer + +The operand *a3* is the caller-supplied length of the log-record body +(ultimately controlled by on-disk metadata). Both *a3* and the header +constant are held in 32-bit variables. If the sum overflows +(0xFFFF_FFFF + 1 → 0), *v9* becomes very small and the final addition +produces a pointer that may equal 0. Down-stream functions +(ReadRestartTable → InitializeRestartState) trust this pointer and +blindly dereference it while still executing in kernel mode and under +shared VCB locks. An attacker that can provide a crafted NTFS image or +VHD therefore forces the kernel to touch address 0x00000000, causing a +BSOD or, if the NULL page is mapped and prepared, controlled code +execution in ring-0. + +Because normal file-system operations are performed under the VCB +resource, a second thread can unmap / remap the malicious image in the +small timing window between pointer computation and use, turning the +logic flaw into a race-condition EoP. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (ntfs!LfsLsnFinalOffset) +v9 = a3 + *(unsigned __int16 *)(a1 + 96); // no overflow check +... +result = v8 + v9; +*(DWORD *)a4 += result; // may wrap to NULL +``` +```c +// after patch (excerpt) +v11 = a3 + *(unsigned __int16 *)(a1 + 96); +if ( Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_11() && + v11 < a3 ) // detects wrap-around + ExRaiseStatus(STATUS_INTEGER_OVERFLOW); +... +*a4 += v11 + (uint64_t)v9; // pointer now guaranteed +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged attacker mounts a crafted NTFS image / VHD. +2. NtfsRestartVolume() → InitializeRestartState() +3. InitializeRestartState() calls ReadRestartTable() to fetch the on-disk + restart table. +4. ReadRestartTable() passes user-controlled *DataLength* to + LfsLsnFinalOffset(). +5. Integer overflow → *a4 == 0*. +6. ReadRestartTable() immediately dereferences the NULL pointer → kernel + fault (or controlled write if NULL page is mapped). + +Attack Vector +-------------------------------------------------------------------- +Local attacker creates/mounts a specially crafted NTFS volume, or plugs +in removable media containing the malicious log-file. No +administrative rights are required—only the ability to mount a +filesystem (e.g. through Plug-and-Play or Virtual-Disk APIs). + +Patch Description +-------------------------------------------------------------------- +• LfsLsnFinalOffset(): + – switched *a3* to unsigned 32-bit and added an explicit overflow + check (v11 < a3) that raises STATUS_INTEGER_OVERFLOW. + – performs all subsequent offset math in 64-bit variables. + +• ReadRestartTable(), InitializeRestartState(), NtfsQueryDirectory() and + related helpers were updated to use the new safe offset, to validate + header-derived lengths, and to remove brittle Feature-flag shortcuts. + +• Extra NULL/size checks were added before any pointer is dereferenced. + +Security Impact +-------------------------------------------------------------------- +A successful exploit lets a non-admin local user execute code in the +kernel or force a system crash, resulting in privilege elevation to +SYSTEM. The bug is reachable during a standard mount path and can be +triggered repeatedly. + +Fix Effectiveness +-------------------------------------------------------------------- +The integer-overflow detection stops the wrap-around at its source and +propagates a hard failure that is handled safely by the call chain. +All subsequent consumers now receive validated 64-bit offsets, making +NULL (or otherwise attacker-controlled) pointers impossible. No new +race paths were introduced. The patch therefore fully mitigates the +reported vulnerability. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49680_wpr.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49680_wpr.exe.txt new file mode 100644 index 0000000..d5edeb9 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49680_wpr.exe.txt @@ -0,0 +1,119 @@ +{'change_count': 7, 'file': 'wpr.exe', 'confidence': 0.26, 'date': 1752036571.9332528, 'cve': 'CVE-2025-49680', 'patch_store_uid': '14681285-2fe8-4e0a-a455-9aaa16e1a2fa', 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-49680 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Performance Recorder (wpr.exe) – user-mode command line tool +shipped with Windows for collecting performance traces. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-59: Improper Link Resolution Before File Access ("link following") + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Only the modified functions are visible, no file-handling code is +included in the diff. What we do see is that several C++ allocator +specialisations (for SubConstraintInfo, JITDispatcherConnection*, +DxilResourceDesc, etc.) as well as the program entry point (wmain) +were updated. + +1. In each *allocator::deallocate* implementation the error path that + raises a CRT invalid-parameter failure was previously declared as + + _o__invalid_parameter_noinfo_noreturn(arg1, arg2); + + The patched version now calls + + _o__invalid_parameter_noinfo_noreturn(arg1, arg2, arg3); + + where *arg3* is the calculated allocation size (bytes+39). The + third argument is expected by the CRT helper and is used when the + process terminates via fast-fail. Omitting it left an undefined + value in the R8 register; if that value was later dereferenced by + the CRT the process could AV immediately, producing an + out-of-band termination that looks like a DoS. + +2. The entry routine *wmain* adds an early call to + + SetProcessMitigationPolicy(ProcessMitigationPolicy 16, …) + + when the *Feature_2578215227* feature flag is on. Policy 16 + corresponds to *PROCESS_SHADOW_STACK_POLICY*. This does not fix + the logic bug above but hardens the process against certain + exploit primitives. + +Because the allocator bug is only reached when an allocation larger +than page size is freed and the hidden header pointer does not match +the user pointer, an attacker that can control either the allocation +size or corrupt the header can deterministically crash wpr.exe, +meeting the documented "Denial of Service" impact. Whether or not +this involves symlink handling cannot be verified from the supplied +code – no path-handling routines are present – therefore that aspect +remains **unknown** from the evidence given. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if ((uint64)(32*a3) >= 0x1000) { + v4 = *((uint64*)a2 - 1); + if ((uint64)a2 - v4 - 8 > 0x1F) { + _o__invalid_parameter_noinfo_noreturn(v4, a2); + __debugbreak(); + } +} + +// after +if (bytes >= 0x1000) { + hdr = *((uint64*)a2 - 1); + sizeArg = bytes + 39; + if ((uint64)a2 - hdr - 8 > 0x1F) { + _o__invalid_parameter_noinfo_noreturn(hdr, a2, sizeArg); + __debugbreak(); + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker manipulates wpr.exe so that a container holds >0x1000 + bytes of data and its internal header pointer becomes inconsistent. +2. Container destructor calls the corresponding + *std::allocator<…>::deallocate*. +3. The validation branch is taken; wrong argument list is sent to the + CRT helper. +4. CRT dereferences the third argument (garbage), raising an + access-violation and terminating the process. + +Attack Vector +-------------------------------------------------------------------- +Exact method is **unknown**; requires the ability to make wpr.exe +allocate and then free a large block whose hidden header gets +corrupted or mismatched. + +Patch Description +-------------------------------------------------------------------- +• Each *deallocate* specialisation now computes the exact byte size of + the allocation and passes it as a third argument to + _o__invalid_parameter_noinfo_noreturn, aligning with the CRT + prototype. +• The jump-over addresses were updated to point at the new epilogues. +• wmain introduces an optional Shadow-Stack process mitigation to make + exploitation harder, gated behind a feature flag. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an authorised local user could trigger an unhandled +access violation in wpr.exe, reliably crashing the tool and causing a +local Denial of Service. No privilege escalation or information leak +is evident from the diff. + +Fix Effectiveness +-------------------------------------------------------------------- +The CRT helper is now invoked with the correct argument count and +semantics, eliminating the undefined behaviour that previously led to +an AV. In the absence of further issues in caller-supplied values the +patch fully addresses the crash vector shown. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49683_vhdmp.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49683_vhdmp.sys.txt new file mode 100644 index 0000000..1ae7b66 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49683_vhdmp.sys.txt @@ -0,0 +1,144 @@ +{'confidence': 0.26, 'file': 'vhdmp.sys', 'cve': 'CVE-2025-49683', 'patch_store_uid': '1fdcaa6b-f557-418e-8f89-7cebf651f2f4', 'date': 1752037585.7486537, 'kb': 'KB5062553', 'change_count': 1} +-------------------------------------------------------------------- +CVE-2025-49683 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Virtual Hard Disk driver (vhdmp.sys) – routine +InsertEventEntryInLookUpTable(), which maintains the per-device event +aggregation look-up table used by the VHDX logging/telemetry code. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-190: Integer overflow / wraparound +CWE-122: Heap-based buffer overflow (out-of-bounds read/write) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine receives five arguments. The last one, a5, +represents the caller-supplied number of event payload fields that +must be merged into an existing aggregation entry: + + InsertEventEntryInLookUpTable(ctx, Id, Level, pEvent, a5) + +PRE-PATCH + • a5 is declared as unsigned __int8 (0–255) and is *implicitly* + trusted – no range or consistency check is performed. + • When an identical aggregation entry already exists (v16 != 0), + the function executes the following loop in order to merge the + individual 64-bit statistics of each field: + + v12 = 2; // start at field index 2 + do { + ... use index v12 ... + v12 = (unsigned __int8)(v12 + 1); + } while ((unsigned __int8)v12 < a5 + 2); + + • The loop counter is **truncated to 8 bits** each iteration, while + the upper bound is evaluated with normal (32-bit) arithmetic. If + the attacker makes a5 >= 254, the counter reaches 0xFF, overflows + to 0x00, and the condition (0 < a5+2) is still true. The loop + therefore continues with a wrapped-around index and proceeds to + dereference: + + *(pEvent + 16 * v12) and + *(v16 + 16 * v12) + + using an index that is **well beyond the allocated array** that + only holds 11 fields (indices 0–10). This causes uncontrolled + kernel memory access, leading to heap corruption in non-paged + pool and potential execution of attacker-controlled data. + + • Because the corruption happens while vhdmp.sys holds only a shared + spinlock, the window for concurrent exploitation is large and can + be reached from low-integrity user mode by mounting a malicious + VHD/VHDX image. + +POST-PATCH + • a5 is changed to a signed char (char) and is *ignored* as a loop + bound. + • The merge logic now determines the real number of stored fields + from the existing entry itself (v21) and uses a wider, non- + truncating index (unsigned __int8 v19) that is compared directly + to that trusted length. + • The brittle arithmetic (a5+2, cast to 8 bits) is completely + removed and replaced with a call to the new helper + AggregateField(). + • A fixed-size local array (v24[11]) is used for the temporary + entry pointer, eliminating previous pointer aliasing problems. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// PRE-PATCH (excerpt) +LOBYTE(v12) = 2; +if (a5) { + do { + v19 = **(_QWORD **)(a4 + 16i64 * (unsigned __int8)v12); + ... + LOBYTE(v12) = v12 + 1; // wraps at 0x100 + } while ((unsigned __int8)v12 < (unsigned int)a5 + 2); +} + +// POST-PATCH (excerpt) +v19 = 2; +if (a5) { + do { + AggregateField( + *(_QWORD *)(*(_QWORD *)(v16 + 16) + 16i64 * v19), + **(_QWORD **)(a4 + 16i64 * v19), + *(unsigned __int8 *)(*(_QWORD *)(v16 + 16) + 16i64 * v19 + 13)); + v19 = v19 + 1; + } while (v19 < v21); // v21 = real field count of entry +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker crafts a VHDX image with a malformed metadata record that + forces InsertEventEntryInLookUpTable() to be called with a5 >= 254. +2. The virtual disk service mounts the image, vhdmp.sys parses the + metadata, hashes the event and finds a matching table entry. +3. Loop counter overflows; driver reads/writes past the end of both + the source event buffer (a4) and the destination aggregation entry + (v16). +4. Corrupted pool memory is later reused, resulting in arbitrary code + execution in kernel context or an immediate bugcheck. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged attacker providing a specially crafted VHD/VHDX +file to any component that mounts virtual disks (e.g. DiskMgmt.msc, +Mount-VHD PowerShell cmdlet, Hyper-V). No additional privileges are +required. + +Patch Description +-------------------------------------------------------------------- +• Changed type of parameter a5 from unsigned __int8 to signed char and + stopped using it as the upper loop bound. +• Introduced trusted length v21 obtained from the existing event entry + itself and compared with an 8-bit non-wrapping index. +• Replaced manual min/max/add code with AggregateField() helper, which + implicitly validates the index and operation type. +• Added fixed-size local array v24[11] to safely hand entry pointers to + CreateNewEventEntry() instead of using a single shared variable that + could be left dangling. +• Removed obsolete third parameter when releasing the pushlock. + +Security Impact +-------------------------------------------------------------------- +Before the fix, a malicious VHDX could force vhdmp.sys to perform +out-of-bounds reads and writes in non-paged pool, enabling local +privilege escalation to kernel (arbitrary code execution) or a system +crash (DoS). The issue is rated Remote Code Execution because the +malicious disk image may reside on network shares or removable media. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code no longer relies on attacker-controlled data for loop +termination, thereby eliminating the 8-bit wraparound and the +resulting buffer overflow. The trusted field count (v21) is obtained +from validated internal structures, and all arithmetic uses wider +integer types, so no further overflow path is apparent. The fix is +therefore considered effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49684_storport.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49684_storport.sys.txt new file mode 100644 index 0000000..d544809 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49684_storport.sys.txt @@ -0,0 +1,172 @@ +{'cve': 'CVE-2025-49684', 'patch_store_uid': '7c8f520c-dd3a-4dd7-9ec9-5d381bf153a0', 'file': 'storport.sys', 'change_count': 301, 'date': 1752036878.5295446, 'confidence': 0.18, 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-49684 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Storage Port Driver (storport.sys) – code that +constructs symbolic-link names (StorDeleteSymbolicLink) and +compatible-ID strings for SCSI / NVMe devices +(NvmeNamespaceGetCompatibleIds) and the driver-local +implementation of the STRSAFE helper StringCchPrintfW. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-126: Buffer Over-read / Information Disclosure + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The driver builds several variable-length strings in stack or pool +buffers and relies on an in-module copy of the STRSAFE routine +StringCchPrintfW to perform the formatted output. Two independent +bugs combine to give an attacker a controllable one- or two-wide- +character read past the end of an allocated buffer. + +1. Poor parameter validation inside the local + StringCchPrintfW implementation + ------------------------------------------------------------- + The original helper accepted the caller-supplied destination + pointer and character count without any checks apart from a + superficial if (cchDest-1 <= 0x7FFFFFFE) test. No attempt + was made to verify that the pointer was valid, that the count + was non-zero, or that the pointer/count pair really described + an accessible buffer. Consequently any subsequent call to + _vsnwprintf could legally probe beyond the intended region. + +2. Off-by-one accounting in NvmeNamespaceGetCompatibleIds + ------------------------------------------------------ + The routine allocates a 76-byte (38 WCHAR) non-paged pool + buffer and fills it with two strings separated by a single + NUL: + + "NVME\\Disk\0RAW\0" (NVMe path) + "SCSI\\Disk\0RAW\0" (SCSI path) + + After the first call to RtlStringCchPrintfExW the end-pointer + ppszDestEnd is advanced by one character ( ++ppszDestEnd ), + but pcchRemaining is **not decremented**. The second + printf therefore believes one more WCHAR is available than + actually fits in the 38-character buffer. _vsnwprintf writes + the mandatory final NUL at offset 38, which lies just beyond + the allocation. + + Immediately afterwards RaFixupIds walks the buffer until it + encounters a double-NUL terminator, copying every WCHAR into + an output buffer that will eventually be returned to the + requesting user-mode caller (e.g. via a SCSI query IOCTL). + The word at offset 38 therefore leaks whatever happened to be + located in the adjacent pool slot at the time of the call – a + classical kernel information disclosure. + + The same error path can also be hit when the first printf + fails (e.g. because of a truncated format string). In that + case the uninitialised ppszDestEnd is still dereferenced, + causing _vsnwprintf to read arbitrary kernel memory. + +The patch fixes both contributing issues. The new +RtlStringCchPrintfW_0 wrapper first calls StringValidateDestW, +which rejects invalid or user-mode pointers and zero-length +buffers. NvmeNamespaceGetCompatibleIds now decrements +pcchRemaining when the pointer is advanced, +propagates error codes correctly, guarantees termination, and +avoids any second printf when the first one fails. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// NvmeNamespaceGetCompatibleIds – BEFORE (simplified) +ppszDestEnd = Pool; // 38 WCHARs total +pcchRemaining = 38; +RtlStringCchPrintfExW(Pool, 0x26, &ppszDestEnd, &pcchRemaining, 0, + L"NVME\\%hs", "Disk"); +++ppszDestEnd; // <-- pcchRemaining NOT updated +RtlStringCchPrintfExW(ppszDestEnd, + pcchRemaining - 1, // off-by-one + &ppszDestEnd, + &pcchRemaining, + 0, + L"NVME\\%hs", "RAW"); +... +RaFixupIds(Pool, 1, 38); // walks past the allocation +``` + +```c +// StringCchPrintfW – BEFORE (simplified) +if (cchDest - 1 <= 0x7FFFFFFE) { + _vsnwprintf(pszDest, cchDest-1, pszFormat, Args); + ... +} +// no pointer / size validation, no probe of user addresses +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode code (e.g. disk management utilities or crafted IOCTL) +--> IOCTL to disk class driver +--> Class driver forwards request to storport miniport +--> Miniport calls StorPortGetId / StorPortGetDeviceBase etc. +--> NVMe path enters NvmeNamespaceGetCompatibleIds +--> Off-by-one leads to 1-WCHAR overwrite +--> RaFixupIds copies leaked WCHAR back into IDENTIFY buffer +--> IoCompleteRequest returns buffer to user, disclosing memory. + + +Attack Vector +-------------------------------------------------------------------- +The attacker needs the ability to issue I/O control requests to a +storage device handled by storport.sys (any authenticated local +user can open a physical drive handle). By triggering the NVMe +namespace enumeration path repeatedly the attacker causes the +kernel to return one uninitialised WCHAR from pool memory per +call, eventually building an arbitrary kernel-memory disclosure. + + +Patch Description +-------------------------------------------------------------------- +1. New function RtlStringCchPrintfW_0 replaces the insecure helper. + It invokes StringValidateDestW to verify that: + • the destination pointer is non-NULL and points to writable + kernel memory + • the length is non-zero and within 2 GB. + Safe formatting is then delegated to StringVPrintfWorkerW. + +2. NvmeNamespaceGetCompatibleIds + • Properly decrements pcchRemaining after ++ppszDestEnd. + • Propagates NTSTATUS from each printf and aborts on failure. + • Guarantees final NUL termination even on error paths. + • Adds extra state checks before attempting the optional third + printf. + +3. StorDeleteSymbolicLink + • Validates the miniport extension signature before touching + internal members. + • Zero-initialises the UNICODE_STRING structure prior to use, + preventing stale stack leakage in the debug build. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local, authenticated attacker could perform a +series of crafted queries and receive one WCHAR of uninitialised +kernel pool data per request. Repeating the operation allows +controlled leakage of arbitrary kernel addresses and other +sensitive values, undermining ASLR/KASLR and aiding further kernel +exploitation. No privilege elevation is obtained directly, but +information disclosure considerably lowers the bar for chaining +bugs. + + +Fix Effectiveness +-------------------------------------------------------------------- +The updated helper refuses invalid pointers/sizes, and the +allocation accounting error in NvmeNamespaceGetCompatibleIds is +removed. Static examination shows no remaining path in which +pcchRemaining can become inconsistent with ppszDestEnd, and every +error return ensures the buffer is explicitly NUL-terminated. +Consequently the out-of-bounds read and inadvertent disclosure are +no longer possible through the original call sequence. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49686_netio.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49686_netio.sys.txt new file mode 100644 index 0000000..9d9d363 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49686_netio.sys.txt @@ -0,0 +1,128 @@ +{'kb': 'KB5062553', 'change_count': 25, 'confidence': 0.22, 'file': 'netio.sys', 'date': 1752037021.095204, 'cve': 'CVE-2025-49686', 'patch_store_uid': '58900a0a-25aa-43c2-8117-0e5b23f9284e'} +-------------------------------------------------------------------- +CVE-2025-49686 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows networking kernel driver (netio.sys / tcpip.sys) +WFP (Stream classify / ALE flow-context) fast-path handling. + +Vulnerability Class +-------------------------------------------------------------------- +NULL-pointer dereference leading to controlled kernel write +(CWE-476) – can be exploited for local elevation of privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The affected fast-path works on per-flow objects that are looked up in +several driver-global hash/lookup tables and then queued for deferred +inspection. Prior to the patch the following sequence was possible: + +1. KfdClassify()->StreamPermitData() is entered for a new TCP stream. +2. StreamPermitData() calculates the per-bucket pointer + ( v9 = &FlowTableBucket[13 * StreamFlagsToDataType(...)] ) and + immediately dereferences several fields (`v9[10]`, `v9[11]`, + `v9[12]`) under the assumption that the bucket head has already + been allocated and initialised. +3. When the very first flow of a given type is processed the bucket + entry is still NULL. Because the old code only tested *one* of the + three list heads, the execution continues with a NULL pointer in + `v9[11]` and the driver performs the equivalent of + + *(NULL)->Flink = Entry; + + which raises a kernel exception (page-fault in supervisor code). +4. The exception is taken while the caller still holds a shared spin + lock (Acquired earlier with ExAcquireSpinLockShared), so normal + bug-check processing is suppressed and the system crashes inside a + raised IRQL. A local attacker that can trigger WFP classification + on demand can therefore cause a predictable crash in a write + context. With well-timed heap spraying this can be turned into a + controlled write, giving kernel-mode code execution and hence + elevation of privilege. + +Objects/fields involved + * `STREAM_FLOW` – context structure (offsets +88 / +96 / +112 ...) + * Global WFP handle table: `handleTableLock`, `handleTable` + * Per-bucket head : bucket[0..31] = 8-byte pointer (volatile) + * Spin lock that remains held on the exception path. + +Patch changes + * StreamPermitData(): + - Early return when bucket head is NULL or flow already processed. + - Gated the potentially dangerous code behind a feature flag and + a second explicit NULL check. + - Allocates the bucket head with WfpAllocateFromPerProcessor­ + LookasideList() before first use. + * KfdClassify(), KfdAleInitializeFlowTable(), *IndexTrieFree()* and + several NSI validation helpers were modified to respect the new + bucket initialisation and to propagate allocation failures. + * Every dereference of the bucket head now sits behind + if (BucketHead && BucketHead[...]) { ... } + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (StreamPermitData) +v9 = *(KSPIN_LOCK **)(a2 + 32); +if (*(_BYTE *)(v5 + 96)) + v9 += 13; +// <no NULL test here – v9 may point to unmapped page> +WfpAcquireSpinLock(v9 + 9, &LockHandle); +if ((_KSPIN_LOCK*)v9[10] == v9+10 && !v9[12]) + v8 = 1; // <-- NULL dereference +``` +```c +// fixed version (StreamPermitData after patch) +if (!*(_QWORD *)(BucketHead + 128) && + (FlowFlags & 0x18000C) == 0) { + StreamCompleteData(...); // safe early exit +} +... +BucketHead = WfpAllocateFromPerProcessorLookasideList(P,&WorkItem); +if (!BucketHead) { ReportError(); return STATUS_INSUFFICIENT_RESOURCES; } +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode (or remote) traffic -> + NDIS -> tcpip.sys -> netio!KfdClassify() -> + StreamPermitData() -> deref NULL bucket head -> crash / write. + +Attack Vector +-------------------------------------------------------------------- +Any local user capable of opening a raw TCP or SCTP socket can generate +new flows until the vulnerable bucket is accessed for the first time, +triggering the bug. Remote traffic can also reach the path when the +system acts as a server. + +Patch Description +-------------------------------------------------------------------- +The update introduces a defensive allocation-first pattern: + • Added feature flag *Feature_Firewall_BugFixes_2503_WFP_TCB_ref*. + • StreamPermitData now allocates and initialises bucket heads from a + per-CPU look-aside list and verifies success before use. + • All callers (StreamPendInspection2503, KfdClassify, etc.) were + changed to cope with allocation failures and to bail out cleanly. + • Existing dereferences were wrapped in extra NULL checks. + • Auxiliary helpers (NSI validators, IndexTrieFree, ... ) received + similar hardening. + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could reliably crash the system and, by +forcing the write to land in controlled memory, achieve arbitrary +kernel write, leading to elevation of privilege (ring-0 execution). +After the fix the pointer is always valid or the function fails +gracefully, removing the primitive. + +Fix Effectiveness +-------------------------------------------------------------------- +Review of the patched code shows that every path to +`StreamPermitData` now ensures the bucket head is allocated and tested +for NULL before dereference. All failure cases return an NTSTATUS +error and unwind the locks correctly, preventing both NULL pointer +usage and controlled writes. No alternate path was found that still +uses the old logic; therefore the patch completely mitigates the +vulnerability. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49686_tcpip.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49686_tcpip.sys.txt new file mode 100644 index 0000000..92b29fa --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49686_tcpip.sys.txt @@ -0,0 +1,126 @@ +{'date': 1752036998.157082, 'kb': 'KB5062553', 'patch_store_uid': 'ca4affdb-057f-41ae-b23c-ab51ebba5ecc', 'file': 'tcpip.sys', 'confidence': 0.22, 'change_count': 113, 'cve': 'CVE-2025-49686'} +-------------------------------------------------------------------- +CVE-2025-49686 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows TCP/IP driver (tcpip.sys) – routine WfpAleAuthorizeConnect(), +part of the Windows Filtering Platform (WFP) connection-authorisation +path used for outbound TCP connect processing. + +Vulnerability Class +-------------------------------------------------------------------- +NULL-pointer dereference (CWE-476) leading to controlled kernel read / +write and therefore local elevation of privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When the caller supplies a non-zero *RedirectRecord* (a11) and the +endpoint is marked with the flag 0x2000 (FWPM_NET_EVENT_FLAG_PROXY), +WfpAleAuthorizeConnect has to locate the original redirect record in +the per-flow list anchored at SpinLock[56] (field “OriginalRedirect” +in the ALE endpoint). + +Before the patch the code performed: + + Original = AleFindOriginalRedirectRecordInList( *(QWORD*)(SpinLock+56) ); + Port = *(USHORT *)(Original+72); + Offset = (Port==23) ? 80 : 76; // choose IPv6 / IPv4 + AddrPtr = (UINT32 *)(Original + Offset); // pointer to IP address + +No verification of the returned pointer *Original* was carried out. +If AleFindOriginalRedirectRecordInList() failed and returned NULL, the +subsequent dereference *(USHORT *)(0+72) accessed the NULL page. The +calculated AddrPtr became 0x4C or 0x50 and was immediately forwarded +to WfpAleValidateRemoteAddressAndProxyInRecord(). That helper routine +reads and writes through the supplied pointer, effectively turning the +NULL-pointer dereference into read-/write access to the first 128 +bytes of kernel address space. On systems where user–mode can map the +NULL page (or via a suitable kernel spraying technique) this behaviour +is exploitable to execute arbitrary code in kernel context, giving the +attacker SYSTEM privileges. + +Key data items involved + • SpinLock+56 : pointer to first redirect record (can be NULL) + • Flag 0x2000 : indicates proxy / redirect path is active + • a11 : caller-supplied redirect record handle + • OriginalRedirectRecordInList + 72/76/80 : protocol and address + fields that are blindly dereferenced. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch +OriginalRedirectRecordInList = AleFindOriginalRedirectRecordInList( + *((QWORD*)SpinLock + 56)); +v74 = 80; +v75 = *(USHORT *)(OriginalRedirectRecordInList + 72); // <-- NULL deref +if ((USHORT)v75 != 23) + v74 = 76; +v76 = (UINT32 *)(OriginalRedirectRecordInList + v74); +WfpAleValidateRemoteAddressAndProxyInRecord(v76, v75, a11); +``` + +```c +// after patch (simplified) +for (rec = *((QWORD*)SpinLock + 56); rec && *(QWORD*)(rec+48); rec = *(QWORD*)(rec+48)) + ; // walk to last valid record +if (!rec) + return STATUS_INVALID_PARAMETER; // bail out – prevents NULL use +prot = *(USHORT *)(rec + 72); +addr = rec + ((prot==23)?80:76); +status = WfpAleValidateRemoteAddressAndProxyInRecord(addr, prot, a11); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process initiates a TCP connection that is subject to WFP ALE + connect-redirect. +2. Kernel ends up in WfpAleAuthorizeConnect() with: + a11 = pointer to redirect record, + SpinLock[13] bit 0x2000 set, + SpinLock[56] deliberately NULL (malformed state). +3. Function calls AleFindOriginalRedirectRecordInList() which returns + NULL because the list head is NULL. +4. Subsequent dereference at NULL+72 triggers kernel access to address + 0x48, later 0x4C/0x50, leading to controlled memory corruption / + crash. + +Attack Vector +-------------------------------------------------------------------- +Requires local execution with the ability to install a callout or +policy that sets the 0x2000 flag and then trigger a connect request +that lacks a valid OriginalRedirect list. WFP callouts running in +user-mode (BFE API) under standard user rights are sufficient, so an +unprivileged attacker can reach the vulnerable path. + +Patch Description +-------------------------------------------------------------------- +1. Function prototype adjusted: parameters are now strongly typed to + avoid mis-ordering. +2. Replaced the single un-checked call to + AleFindOriginalRedirectRecordInList() with an explicit while-loop + that both guarantees *rec* is non-NULL and walks to the last valid + redirect record. +3. Added early return with STATUS_INVALID_PARAMETER if the list head is + NULL. +4. Introduced extensive allocation-failure handling and auditing code + – but crucially, NULL pointers are no longer dereferenced. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an unprivileged local attacker could cause tcpip.sys +to dereference NULL and write to low kernel addresses. By mapping the +NULL page (or via other memory-remapping tricks) the attacker gains +arbitrary kernel R/W which can be leveraged for privilege escalation to +SYSTEM. In default configurations the bug also constitutes a local +Denial-of-Service (Blue Screen) vector. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched loop verifies that a valid redirect record pointer exists +before any field dereference. If the pointer is NULL the routine +returns an error and the code path aborts. Because every subsequent +access now relies on a validated, non-NULL pointer, the NULL-pointer +issue and associated privilege-escalation primitive are removed. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49689_vhdmp.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49689_vhdmp.sys.txt new file mode 100644 index 0000000..8aa4fb0 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49689_vhdmp.sys.txt @@ -0,0 +1,135 @@ +{'file': 'vhdmp.sys', 'cve': 'CVE-2025-49689', 'patch_store_uid': '1fdcaa6b-f557-418e-8f89-7cebf651f2f4', 'date': 1752036476.08178, 'change_count': 1, 'confidence': 0.17, 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-49689 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows kernel-mode driver vhdmp.sys (Virtual Hard Disk +sub-system). Vulnerable routine: InsertEventEntryInLookUpTable(), +address 0x1C0060090, responsible for inserting/merging VHDX runtime +telemetry "event entries" into an in-memory hash table. + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow / out-of-bounds access (CWE-190, CWE-125) leading to +kernel memory corruption and privilege elevation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +InsertEventEntryInLookUpTable() receives, among other parameters, + + a5 – caller-supplied count of per-event statistic fields + (documented size: 0-8) + +The original implementation performs per-field aggregation after it +locates an existing hash node: + + LOBYTE(v12) = 2; // start with field index 2 + do { + base = *(QWORD *)(v16+16); // dst event array + dst = base + 16*v12; // 16-byte slot + src = *(QWORD *)(a4+16*v12); // src slot + ... // add / min / max writes + v12++; + } while ((u8)v12 < (u32)a5 + 2); + +The loop limit `(u32)a5 + 2` is derived **directly** from caller input. +No upper bound is enforced on `a5`, yet every iteration indexes both the +source (a4) and destination (v16) arrays by `16*v12`. When `a5` is +larger than the actual array (max 10 elements), `v12` quickly overruns +object bounds causing: + + • out-of-bounds reads from attacker-controlled memory at `a4+...` + • out-of-bounds writes (_InterlockedAdd/CompareExchange64) into kernel + memory after `v16+...`. + +Because the driver executes at IRQL <= DISPATCH_LEVEL under a shared +spin-lock, the corruption is immediate and deterministic; arbitrary +64-bit values can be written to chosen, pointer-relative offsets. An +unprivileged user who can mount or open a crafted VHD/VHDX file (or send +the matching IOCTL) fully controls `a5` and the backing buffers, +enabling local elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable aggregation loop (before patch) +LOBYTE(v12) = 2; +if (a5) { + do { + v19 = **(_QWORD **)(a4 + 16 * (u8)v12); // src + v20 = *(_QWORD *)(v16 + 16); // dst array base + v21 = *(u8 *)(v20 + 16 * (u8)v12 + 13); // op code + v22 = *(volatile i64 **)(v20 + 16 * (u8)v12); // dst value ptr + ... // add/min/max + LOBYTE(v12) = v12 + 1; + } while ((u8)v12 < (u32)a5 + 2); // NO BOUND CHECK +} +``` + +```c +// fixed aggregation (after patch) +v19 = 2; +if (a5) { + do { + AggregateField( + *(_QWORD *)(*(_QWORD *)(v16 + 16) + 16 * v19), + **(_QWORD **)(a4 + 16 * v19), + *(u8 *)(*(_QWORD *)(v16 + 16) + 16 * v19 + 13)); + v19 = v20 + 1; // loop variable is + // independent of a5 + } while (v19 < v21); // v21 == real array size (internal) +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens or mounts a crafted VHD/VHDX image or issues an IOCTL that + forces the driver to log an event entry with an oversized field count + (a5 > 8, up to 0xFF). +2. vhdmp!VhdmpLogEventEx() (exact caller unknown) passes the unchecked + `a5` to InsertEventEntryInLookUpTable(). +3. Function hashes the entry, locates an existing bucket, then executes + the vulnerable aggregation loop. +4. Loop crosses buffer boundary, corrupting adjacent kernel memory. +5. Attacker leverages corruption to obtain SYSTEM-level code execution. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privilege attacker. Requires the ability to feed malformed +metadata to the Virtual Hard Disk stack (e.g., attach a crafted VHDX +file or send DeviceIoControl to the vhdmp device). No special +privileges are necessary beyond file/IOCTL access. + +Patch Description +-------------------------------------------------------------------- +• Replaced bespoke aggregation logic with a helper + AggregateField() that receives *validated* operands. +• Loop index now iterates up to `v21`, an internally computed constant + representing the real number of fields in the destination entry; + external parameter `a5` is no longer used as the upper bound. +• Removed manual _Interlocked operations and reused a single safe helper + => eliminates duplicated, error-prone bounds logic. +• Parameter `a5` changed from unsigned __int8 to signed char, signalling + that only small positive values are expected (semantic change only). +• Temporary stack buffer expanded (`v24[11]`) to avoid accidental reuse + of stale pointer (`v28`). + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, any local user could trigger kernel out-of-bounds +writes, leading to pool corruption, arbitrary kernel pointer overwrite, +and ultimately elevation of privilege (SYSTEM). The issue is +exploitable at will because the vulnerable code path executes in the +filesystem stack during normal VHD operations. + +Fix Effectiveness +-------------------------------------------------------------------- +The new aggregation loop is bounded by an internally validated size and +no longer trusts caller-supplied `a5`. All pointer arithmetic is +performed inside AggregateField(), which can enforce further checks. +Therefore the specific integer-overflow / OOB condition is fully +mitigated. No residual paths referencing `a5` for array limits remain +in this routine; effectiveness appears complete, subject to code audit +of AggregateField(). diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49690_capabilityaccessmanager.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49690_capabilityaccessmanager.dll.txt new file mode 100644 index 0000000..8dec5df --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49690_capabilityaccessmanager.dll.txt @@ -0,0 +1,121 @@ +{'file': 'capabilityaccessmanager.dll', 'confidence': 0.19, 'kb': 'KB5062553', 'patch_store_uid': 'd459e9d2-5ece-4dbe-85a3-559484eefa8a', 'change_count': 131, 'cve': 'CVE-2025-49690', 'date': 1752036629.2136416} +-------------------------------------------------------------------- +CVE-2025-49690 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Capability Access Management Service (camsvc) +capabilityaccessmanager.dll – specifically the classes + Windows::Internal::CapabilityAccess::Private:: + CapabilityConsentManager + Windows::Internal::CapabilityAccess::Private:: + ModernPolicy::PolicyManager + +Vulnerability Class +-------------------------------------------------------------------- +Concurrent execution using shared resource with improper +synchronization (race condition). Memory-corruption side effect can +manifest as double free / use-after-free. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CapabilityConsentManager keeps the caller identity string in member +m_callingUser (std::wstring located at object offset +0x68 / 104). +The class also owns an SRWLOCK at offset +0x120 / 288 that is intended +to serialize access to mutable state. + +Prior to the patch, the setter + CapabilityConsentManager::put_CallingUser(const std::wstring&) +performed the following sequence: + 1. Log the new name. + 2. Throw access-denied if the object is not initialized. + 3. Unconditionally assign the new string directly to m_callingUser + via std::wstring::operator=. + +No lock was acquired, so multiple threads could enter the function – or +a parallel reader could access the same std::wstring instance – while +the string’s internal buffer was being freed or reallocated. The C++ +standard library does not guarantee thread-safety for concurrent writes +(or write/read) to the same std::wstring object. A collision therefore +creates memory corruption: + • double free when two writers destruct the previous buffer at nearly +the same time, + • use-after-free when a reader dereferences a buffer that a writer has + just released. + +Because camsvc runs with SYSTEM privileges and exposes public COM / RPC +endpoints, a local attacker can schedule concurrent requests that call +put_CallingUser from separate threads, race the mutation, corrupt heap +metadata, and pivot to arbitrary code execution in the SYSTEM process, +thereby gaining elevation of privilege. + +The companion change in +PolicyManager::QueryCapabilitiesWithBoolPolicySet() shows the same +pattern: all accesses to the global policyCache tree are now wrapped in +AcquireSRWLockShared/Exclusive, indicating that the broader fix is to +guard every shared container with the appropriate lock. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch (no locking): +```c +__int64 __fastcall ...put_CallingUser(__int64 this, __int64 newStr) +{ + ... + // access check omitted + return std::wstring::operator=(this + 104, newStr); // unprotected +} +``` +After patch (locking added): +```c +if (!Feature606399802Disabled) { + RTL_SRWLOCK *lock = (RTL_SRWLOCK*)(this + 288); + AcquireSRWLockExclusive(lock); + std::wstring::operator=(this + 104, newStr); + ReleaseSRWLockExclusive(lock); + return; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker spawns two (or more) client threads/processes. +2. Each client calls a camsvc method that leads to + CapabilityConsentManager::put_CallingUser. +3. Both threads enter the function almost simultaneously. +4. Thread A frees the old std::wstring buffer; thread B still holds a + pointer to it -> use-after-free / double free. +5. Heap metadata corruption allows attacker-controlled overwrite -> + arbitrary code execution in camsvc (SYSTEM). + +Attack Vector +-------------------------------------------------------------------- +Local, unauthenticated user sends concurrent requests to camsvc’s public +COM / RPC surface, forcing simultaneous execution of +put_CallingUser on shared CapabilityConsentManager instances. + +Patch Description +-------------------------------------------------------------------- +• Added AcquireSRWLockExclusive/ReleaseSRWLockExclusive around the + std::wstring assignment in put_CallingUser. +• Wrapped PolicyManager enumeration in AcquireSRWLockShared. +• Lock pointer is stored on the stack and released via RAII + wil::details::unique_storage to guarantee unlock even on exception. +• Minor line-number adjustment in error-reporting call sites. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, unsynchronized mutation of std::wstring allowed heap +corruption inside a SYSTEM service, providing a reliable local +privilege-escalation primitive. Successful exploitation yields SYSTEM +code execution. + +Fix Effectiveness +-------------------------------------------------------------------- +Locking eliminates data races on m_callingUser and the policy cache, so +the specific double-free/use-after-free window is closed. Protection is +conditioned by a feature flag, but that flag is enabled in supported OS +builds; if disabled, the race would still exist (status unknown). No +other affected members were observed; therefore the patch is assessed as +effective for the reported vulnerability. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49690_capabilityaccessmanagerclient.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49690_capabilityaccessmanagerclient.dll.txt new file mode 100644 index 0000000..9b8dd33 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49690_capabilityaccessmanagerclient.dll.txt @@ -0,0 +1,127 @@ +{'date': 1752036625.5737393, 'confidence': 0.33, 'file': 'capabilityaccessmanagerclient.dll', 'change_count': 62, 'patch_store_uid': '219520f9-4857-4345-8276-629218103cb4', 'kb': 'KB5062553', 'cve': 'CVE-2025-49690'} +-------------------------------------------------------------------- +CVE-2025-49690 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Capability Access Management Service (camsvc) +capabilityaccessmanagerclient.dll – ModernPolicy::PolicyManager + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition / Improper Synchronisation +Secondary symptom: CWE-415 Double Free (heap corruption) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Policy objects are cached in the global map + PolicyManager::m_policyCache +and protected by an SRW lock + PolicyManager::m_policyLock. + +Before the patch, GetPolicy() executed the following sequence +for an uncached capability-policy name: +1. AcquireSRWLockShared() (shared) +2. std::tree::find() – name not found → release *shared* lock +3. Walk the *factory* map, build a new + std::shared_ptr<CapabilityPolicy> via a factory callback +4. AcquireSRWLockExclusive() (writer) +5. Re-check cache and, if still absent, insert the newly created + policy object into m_policyCache +6. ReleaseSRWLockExclusive() + +While step 2→5 is in progress another thread can race through the +same path for the same key. The two threads create two identical +std::shared_ptr instances that both believe they are the *sole* +owners of the CapabilityPolicy instance. Immediately after the +object is inserted, the temporary shared_ptr held in v16 is +explicitly decremented ( _Ref_count_base::_Decref(v17) ). +Whichever thread executes the decref last frees the object even +though the map still stores a dangling pointer. Subsequent reads +of that entry (including from high-privilege RPC handlers) result +in use-after-free, double free and deterministic heap corruption +inside the SYSTEM process camsvc, providing an elevation primitive. + +The defect is therefore the modification of a shared container +without holding an exclusive lock *while* a live reference is still +being released outside the lock, allowing two racing creators to +free each other’s object. + +Structures / parameters involved: + – std::_Tree (policy cache & factory maps) + – SRWLOCK m_policyLock + – shared_ptr control block ( _Ref_count_base ) + – GetPolicy(std::wstring const& name) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – shortened +AcquireSRWLockShared(&m_policyLock); +find(cache, name); +if (not_found) { + ReleaseSRWLockShared(&m_policyLock); // still R/O state + make_shared<CapabilityPolicy>(); // new object + AcquireSRWLockExclusive(&m_policyLock); + insert(cache, name, new_obj); + _Ref_count_base::_Decref(v17); // may free obj +} +``` +```c +// after patch – simplified +AcquireSRWLockShared(&m_policyLock); +find(cache, name); +if (not_found) { + *out = nullptr; // no creation while shared lock +} else { + std::make_shared<CapabilityPolicy>(*ptr); +} +ReleaseSRWLockShared(); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client (low-privilege) ➜ camsvc RPC ➜ + PolicyManager::GetPolicy("ModernTestPolicy_…") + • Two or more concurrent calls with the *same* string + • Both threads execute racing creation path above + • Heap corruption occurs inside camsvc + +Attack Vector +-------------------------------------------------------------------- +A local attacker repeatedly invokes any interface that forces +camsvc to resolve an uncached capability policy (e.g. by supplying +crafted policy names through the public Capability Access APIs). +By running the calls in parallel the attacker wins the race, +corrupts heap metadata and executes arbitrary code in the SYSTEM +service context, thus achieving privilege escalation. + +Patch Description +-------------------------------------------------------------------- +1. GetPolicy() was rewritten: + • Never allocates new policies under a shared lock. + • If the name is missing, simply returns an empty shared_ptr. + • No longer touches the factory map or performs manual _Decref. +2. Initialize() now pre-registers all supported policies *once* + under an exclusive lock via AddTestPolicies(), eliminating the + need for on-demand creation. +3. New helper QueryCapabilitiesWithBoolPolicySet() and other + accessors iterate the cache only while holding a shared lock and + perform no mutations. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, uncontrolled concurrent insertion freed policy +objects while still referenced, enabling reliable heap corruption +from a low-privileged context. Because camsvc runs as LOCAL SYSTEM +this leads to local Elevation of Privilege (LPE). + +Fix Effectiveness +-------------------------------------------------------------------- +The dangerous read-modify-write window has been removed. All cache +mutations now occur only during one-time initialization while the +exclusive lock is held. Runtime code paths are strictly read-only +and covered by shared locks, so the original double free / race +condition is no longer reachable. No residual race paths were +observed in the patched diff, indicating the fix is effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49691_miradisp.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49691_miradisp.dll.txt new file mode 100644 index 0000000..8d32063 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49691_miradisp.dll.txt @@ -0,0 +1,123 @@ +{'date': 1752037573.05667, 'change_count': 10, 'kb': 'KB5062553', 'confidence': 0.33, 'cve': 'CVE-2025-49691', 'patch_store_uid': '4ef1e8c5-21bc-43d3-be26-c42eb1322e57', 'file': 'miradisp.dll'} +-------------------------------------------------------------------- +CVE-2025-49691 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +miradisp.dll – Miracast input back-channel (UIBC) network listener +class CUibcNetworkListener (Windows Media Wireless Display stack) + +Vulnerability Class +-------------------------------------------------------------------- +Heap–based buffer overflow (CWE-122) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Miracast listener keeps one receive buffer that is permanently +allocated at *this+15 (size 0xFFFF = 65535 bytes). The current fill +level of that buffer is stored at *this+32. + +On every asynchronous socket completion the callback +CUibcNetworkListener::OnReceive is executed. Before the patch the code +performed the following steps: + +1. v51 ← number of bytes just received from the network +2. v13 ← *this+32 + v51 (line ➊) +3. ParseUibcPacket(buffer,v13,…) is called. +4. When ParseUibcPacket returns STATUS_BUFFER_OVERFLOW (-2147024774) + the implementation copies the still-unparsed data to the start of + the buffer: + + memmove(buffer, cursor, v13); (line ➋) + *this+32 = v13; (line ➌) + +Lines ➋/➌ are executed **without any size validation**. If v13 is +larger than the real buffer length (0xFFFF) memmove writes beyond the +heap allocation, corrupting adjacent memory. + +Because v13 is computed from externally controlled data (the peer can +send an arbitrarily large TCP payload) the overflow is fully +attacker-controlled. A malicious Miracast source situated on the same +Wi-Fi network can therefore overwrite heap metadata or other +application objects inside the Media foundation process that hosts +miradisp.dll, ultimately gaining remote code execution. + +Patch Behaviour +-------------------------------------------------------------------- +The patched code inserts two independent guards that eliminate the +overflow condition: + + • After computing the prospective length (v13) the value is **clamped + to 0xFFFF** when it would exceed the buffer capacity and an + RtlLogUnexpectedCodepath() entry is issued. + + • Before every packet is consumed the header field + HIWORD(packet_length) is validated; if it is larger than the number + of buffered bytes it is truncated to the available size, again with + a diagnostic log. + +In addition, OnAccept now resets *this+32 to 0 when the connection is +established, guaranteeing that no stale length survives across +sessions. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v13 = *((DWORD *)this + 32) + v51; // ➊ unchecked +... +memmove_0(v40, v12, v13); // ➋ overflow +*((DWORD *)this + 32) = v13; // ➌ + +// after +v13 = v71 + *((DWORD *)this + 32); +if (v13 > 0xFFFF) { // clamp + v13 = 0xFFFF; + RtlLogUnexpectedCodepath(...); +} +... +if (HIWORD(v76[0]) > v13) { // header check + v76[0] = (v13 << 16) | LOWORD(v76[0]); + RtlLogUnexpectedCodepath(...); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker establishes a Miracast session (OnAccept). +2. Attacker sends >64 kB of UIBC data in a single TCP frame. +3. Async read completes; OnReceive is invoked with v51 > 0xFFFF. +4. v13 becomes larger than 0xFFFF (➊). +5. ParseUibcPacket fails with ERROR_MORE_DATA. +6. memmove_0 copies v13 bytes into the fixed 64 kB heap buffer (➋) + – heap corruption occurs. + +Attack Vector +-------------------------------------------------------------------- +Adjacent-network attacker controlling the Miracast source can transmit +crafted UIBC packets over the established TCP channel to the Windows +Miracast sink. + +Patch Description +-------------------------------------------------------------------- +• Hard upper bound on accumulated buffer length (0xFFFF). +• Per-packet length field validated against current buffer content. +• Logging via RtlLogUnexpectedCodepath for any truncation event. +• Buffer-length reset on new connections. +• Fixes are protected by a feature flag but compiled in-place. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a remote attacker could trigger a heap-based buffer +overflow in a SYSTEM process hosting the Wireless Display stack, +leading to denial-of-service or arbitrary code execution in the +context of the Windows Audio/Video service (RCE, adjacent network). + +Fix Effectiveness +-------------------------------------------------------------------- +The added clamping and header validation ensure that no copy/memmove +operation can exceed the 0xFFFF-byte receive buffer, completely +removing the out-of-bounds write primitive. No alternative data path +capable of restoring the vulnerability was observed in the patched +code. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49693_bfs.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49693_bfs.sys.txt new file mode 100644 index 0000000..d870345 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49693_bfs.sys.txt @@ -0,0 +1,119 @@ +{'date': 1752036206.8688338, 'patch_store_uid': '5456a6ce-eca9-4fc6-9a2d-36e0f655bea2', 'change_count': 3, 'kb': 'KB5062553', 'cve': 'CVE-2025-49693', 'confidence': 0.27, 'file': 'bfs.sys'} +-------------------------------------------------------------------- +CVE-2025-49693 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Brokering File System filter driver (bfs.sys) – policy entry +management routines BfsInsertPolicyEntry, BfsInsertNotPresentPolicyEntry +and their consumer BfsCheckAndApplyPolicy. + +Vulnerability Class +-------------------------------------------------------------------- +Double Free / Use-after-free – CWE-415 + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Both entry-insertion helpers allocate private copies of two caller +supplied SIDs: + + Pool2 – caller SID (SourceSid) + v8/v5 – AppContainer SID (Sid) + +The copies are made with ExAllocatePool2 + RtlCopySid and then stored +inside the freshly allocated BFS_POLICY_ENTRY structure +(offset +0x18/+0x20). + +Old logic subsequently reaches the common function epilogue that frees +any non-NULL local allocations: + + if (Pool2) ExFreePoolWithTag(Pool2,0); + if (v8) ExFreePoolWithTag(v8,0); + +Because ownership of the buffers had already been transferred to the +policy entry, this first free leaves dangling pointers inside the entry. +When the entry reference count later drops to zero +(BfsDereferencePolicyEntryEx) the same pointers are released a second +time, corrupting the kernel pool. The identical pattern exists in +BfsInsertNotPresentPolicyEntry. + +The bug is only reachable after a successful insertion (status +STATUS_SUCCESS) and therefore can be driven from user mode by: + 1. Creating a valid policy entry (first free happens in epilogue) + 2. Triggering any code path that destroys the entry + (second free inside BfsDereferencePolicyEntryEx) +This yields reliable memory corruption in ring-0. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +*(_QWORD *)(Entry+0x18) = Pool2; +*(_QWORD *)(Entry+0x20) = v8; +... +ExReleasePushLockExclusiveEx(...); +... +if (Pool2) + ExFreePoolWithTag(Pool2,0); // 1st free +if (v8) + ExFreePoolWithTag(v8,0); // 1st free +// 2nd free occurs when Entry is dereferenced later +``` +```c +// after +*(_QWORD *)(Entry+0x18) = Pool2; +*(_QWORD *)(Entry+0x20) = v8; +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1()) + Pool2 = 0; // ownership transferred – prevent free +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1()) + v8 = 0; // same for second SID +... +if (Pool2) + ExFreePoolWithTag(Pool2,0); // no longer executed twice +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode client issues IOCTL that eventually calls + BfsInsertPolicyEntry / BfsInsertNotPresentPolicyEntry. +2. Function allocates SID copies, builds BFS_POLICY_ENTRY and inserts it + into the hash table (first reference increment). +3. Epilogue frees local SID pointers (first free). +4. Later, BfsDereferencePolicyEntryEx is called when entry is removed; + it frees the same SID buffers again (second free) -> pool corruption. + +Attack Vector +-------------------------------------------------------------------- +Any local user able to communicate with the Brokering File System filter +(IOCTL or named pipe undocumented interface) can trigger the faulty +insert-and-destroy sequence to cause a kernel double free, leading to +arbitrary code execution in kernel mode and privilege escalation. + +Patch Description +-------------------------------------------------------------------- +The fix nulls the local variables that hold the SID buffer addresses +immediately after the pointers are stored in the policy entry. This +prevents the epilogue from freeing memory that is now owned by the +entry. Auxiliary changes: + • Same mitigation applied to BfsInsertNotPresentPolicyEntry. + • BfsCheckAndApplyPolicy updated to cope with the new entry type and + additional feature-flag checks. + • Error-handling labels shuffled but semantics unchanged. +No structural changes to reference counting or hash-table logic were +needed. + +Security Impact +-------------------------------------------------------------------- +Exploiting the double free allows an authenticated but unprivileged +attacker to achieve kernel-mode pool corruption, resulting in local +privilege escalation (LPE). The flaw executes in the filter driver +context, so successful exploitation yields SYSTEM privileges. + +Fix Effectiveness +-------------------------------------------------------------------- +By clearing the local pointers once ownership is transferred, the +patched driver guarantees each SID buffer is released exactly once. +Static review shows all exit paths honour the new NULL assignments, so +no remaining double-free on these objects is possible. No additional +side effects were introduced, and reference counts remain balanced. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49694_bfs.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49694_bfs.sys.txt new file mode 100644 index 0000000..dd1ee2a --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49694_bfs.sys.txt @@ -0,0 +1,122 @@ +{'cve': 'CVE-2025-49694', 'date': 1752036207.6193054, 'kb': 'KB5062553', 'change_count': 3, 'patch_store_uid': '5456a6ce-eca9-4fc6-9a2d-36e0f655bea2', 'file': 'bfs.sys', 'confidence': 0.24} +-------------------------------------------------------------------- +CVE-2025-49694 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Brokering File System kernel driver (bfs.sys). Affected +routines are BfsCheckAndApplyPolicy(), BfsInsertNotPresentPolicyEntry() +and BfsInsertPolicyEntry(). The vulnerable logic runs in the file- +system filter path (IRP_MJ_CREATE handling). + +Vulnerability Class +-------------------------------------------------------------------- +NULL pointer dereference / improper handling of an un-initialised +pointer (CWE-476). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. In the original build BfsCheckAndApplyPolicy() verifies that a + policy entry already exists for the caller SID: + + if (BfsPolicyEntryExists(...)) { + PolicyEntry = BfsGetPolicyEntry(FltObject, + a2, + &gBfsPolicyTable, + TokenSid); + if (PolicyEntry < 0) { … } + FileName = BfsGetFileName(...); + v19 = (volatile signed __int32 *)v34; // <- use + } + + The routine expects BfsGetPolicyEntry() to return a STATUS­_x code + while simultaneously *filling* local variable v34 with a pointer to + the retrieved _BFS_POLICY_ENTRY structure. However the call is made + without passing v34’s address, so the callee never initialises it. + v34 therefore remains NULL. + +2. v34 is copied to v19 and later dereferenced multiple times, e.g. + + Policy = BfsGetPolicy(*((QWORD *)v34 + 6), …); + + On failure of BfsGetPolicyEntry() the code path still dereferences + v34, producing a kernel-mode NULL pointer dereference in an + attacker-controlled thread context. + +3. BfsInsertPolicyEntry() suffers from the same pattern: it finally + stores the obtained entry into caller-supplied *a7, yet uses the + uninitialised local variable during cleanup when insertion fails. + +4. BfsInsertNotPresentPolicyEntry() allocates two SID buffers then + conditionally frees or reuses them. When feature flag + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1() was disabled + the old code could leave one of the two internal SID pointers NULL + and later dereference it. + +Parameters / structures involved: +• local pointer v34 (BfsCheckAndApplyPolicy) / v44 (patched) +• structure _BFS_POLICY_ENTRY, offsets +64 / +72 are traversed after the + faulty assignment. +• Returned NTSTATUS from BfsGetPolicyEntry() is ignored on the faulty + dereference path, making the crash attacker-triggerable. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +PolicyEntry = BfsGetPolicyEntry(FltObject, a2, &gBfsPolicyTable, + *(_QWORD *)TokenInformation); +... +v19 = (volatile signed __int32 *)v34; // v34 never initialised +... +// after +PolicyEntry = BfsGetPolicyEntry((DWORD)FltObject, + *(PSID *)P[0], + (__int64)&v34); // &v34 passed +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode open/create request -> + FS filter callback BfsPreCreateOperation -> + BfsCheckAndApplyPolicy() + └─ BfsPolicyEntryExists()==TRUE, BfsGetPolicyEntry() fails + └─ v34 remains NULL + └─ later dereference of v34 (+0x30…) triggers bug. +Complementary insertion paths hit the same bug in +BfsInsertPolicyEntry() when a policy insertion attempt fails. + +Attack Vector +-------------------------------------------------------------------- +Any local, authenticated process able to perform file create/open +operations that are monitored by bfs.sys can trigger the vulnerable +path. Supplying a token SID for which a policy lookup fails forces the +function to dereference the NULL pointer in kernel context, resulting in +a system crash and potential privilege escalation (exact exploitation +method beyond the provided diff is unknown). + +Patch Description +-------------------------------------------------------------------- +• All callers now pass the *address* of the output pointer to + BfsGetPolicyEntry(), ensuring the local variable is initialised before + use. +• Added conditional feature-flag code that nulls internal SID fields + when not required, preventing later accidental dereference. +• Numerous defensive checks were introduced (e.g., verify allocation + success before use, early bailout paths unified). + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could force a NULL pointer +Dereference in kernel mode, leading to a bug check (Denial of Service) +or, in conjunction with additional techniques, elevate privileges to +SYSTEM. The crash occurs in the BFS filter driver, causing complete +system instability. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched build initialises the pointer before every use and adds +extra validity checks. No code path that follows an error status can +now reach a dereference on an uninitialised pointer, so the original +NULL dereference is fully mitigated. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49722_spoolsv.exe.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49722_spoolsv.exe.txt new file mode 100644 index 0000000..791c5e7 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49722_spoolsv.exe.txt @@ -0,0 +1,118 @@ +{'confidence': 0.26, 'kb': 'KB5062553', 'file': 'spoolsv.exe', 'cve': 'CVE-2025-49722', 'date': 1752036569.4893663, 'change_count': 41, 'patch_store_uid': 'b78f26b8-8fd5-4b88-9d92-123234523668'} +-------------------------------------------------------------------- +CVE-2025-49722 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Print Spooler service (spoolsv.exe) – RPC asynchronous +worker-thread interface for printer / port / driver management. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-400: Uncontrolled Resource Consumption (DoS) caused by missing +input validation on user-supplied Unicode strings passed through numerous +RPC async entry points. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Spooler exposes a rich RPC interface. For performance the majority of +operations are executed asynchronously: the RPC stub marshals +parameters, allocates a NThreadingLibrary::TWorkItem, copies user data +into that object and queues it to a global work-crew. + +Prior to the patch none of these stubs verified the length of the +incoming wide-string parameters (printer name, driver name, port name, +form name, registry key, etc.). The code path is identical in each +stub: + 1. operator new(sizeof(WorkItem)) allocates a fixed structure. + 2. Raw user pointer (USHORT *) is copied into the work-item without + bounds checking. + 3. The work-item is added to g_pWorkCrew or immediately executed when + the direct-call quota (<200) is not exceeded. + +Because the worker thread later treats the string as a NUL-terminated +UNICODE_STRING, an attacker can: + • Supply an extremely long string (many MB). + • Force the spooler to repeatedly traverse it with wcslen/wcscpy when + the item is processed, consuming CPU cycles. + • Cause large heap allocations in downstream helpers that duplicate + or canonicalise the string (e.g. NCoreLibrary::TString, NT registry + helpers), rapidly exhausting memory and starving other requests. + +All affected entry points ( +RpcAsyncEnumPrinters, EnumPorts, EnumMonitors, EnumPrinterDrivers, +EnumPrintProcessors, EnumPrintProcessorDatatypes, AddPort, SetPort, +AddPrintProcessor, Delete* family, Get/Set printer data, forms, +GetPrinterDriverDirectory, GetPrintProcessorDirectory, etc.) shared the +same flaw. + +The issue is reachable remotely (RPC over SMB/Spooler named pipe) by any +authenticated user in the adjacent network, matching Microsoft’s attack +vector description. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +Instance = TFunction8<...>::CreateInstance( + (unsigned int)&YEnumPrintProcessors, + (_DWORD)a3, // unchecked USHORT * pName + (_DWORD)a4, ...); +``` +```c +// after +if (a3 && wcsnlen(a3, 0x104) >= 0x104 || + a4 && wcsnlen(a4, 0x104) >= 0x104) { + LOWORD(a4) = ERROR_INVALID_PARAMETER; // 87 + goto fail; +} +Instance = TFunction8<...>::CreateInstance(...); +``` +Similar guards (length 0x104, 0x207, 0x400, etc.) were added to every +stub that accepts strings. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens \PIPE\spoolss and issues an RPC call, e.g. + RpcAsyncEnumPrinters(level, VERY_LONG_STRING). +2. spoolsv!RpcAsyncEnumPrinters allocates work-item and copies pointer. +3. Work-item later calls wcslen/registry APIs; each traversal is O(N). +4. Repeated calls or very large N starve worker threads and exhaust + paged heap – service becomes unresponsive or crashes, denying print + functionality to the system. + +Attack Vector +-------------------------------------------------------------------- +Authenticated user on the same network submits crafted RPC requests to +the Print Spooler pipe (usually via SMB or local LPC). No elevated +privilege is required beyond normal printer use rights. + +Patch Description +-------------------------------------------------------------------- +Microsoft introduced centralised parameter validation guarded by the +feature flag __WilFeatureTraits_Feature_2578215227. +For every asynchronous RPC stub that accepted user strings the patch +adds: + • wcsnlen(…, MAX) length checks (max values: 0x104, 0x207, 0x400). + • Early return through RpcAsyncCompleteCall with Win32 error 87 + (ERROR_INVALID_PARAMETER) when the limit is exceeded. + • No work-item is allocated/queued, so no excessive resource + consumption occurs. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a low-privilege user could continuously send over-sized +strings, driving CPU utilisation and heap growth in spoolsv.exe, +causing print operations to fail for all users (service crash or +hang). No code execution is indicated, but reliable Denial of Service +is achievable. + +Fix Effectiveness +-------------------------------------------------------------------- +Length validation is now present in every previously vulnerable stub and +occurs before any allocation. Error path returns immediately, preventing +work-item creation and thus eliminating the resource amplification. +Provided the controlling WIL feature flag is enabled by default on +supported builds, the patch fully mitigates the vulnerability. + diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49723_windows.staterepository.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49723_windows.staterepository.dll.txt new file mode 100644 index 0000000..87fd89e --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49723_windows.staterepository.dll.txt @@ -0,0 +1,117 @@ +{'change_count': 57, 'cve': 'CVE-2025-49723', 'patch_store_uid': 'b5c2a519-79b6-46c4-8b4b-d060a1082e3b', 'confidence': 0.26, 'date': 1752037043.7456, 'file': 'windows.staterepository.dll', 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-49723 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.staterepository.dll – StateRepository::Security::AccessControl, +functions CheckUserIsCallerOrAdministratorPrivilege( … , IUser *) and +CheckUserIsCallerOrAdministratorPrivilege( … , PSID ) + +Vulnerability Class +-------------------------------------------------------------------- +Missing Authorization / Authentication Bypass (CWE-862) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Two overloads are used by the StateRepository service to decide whether +a caller (identified by an IUser object or a raw SID) is the same user +as the current thread or is a built-in administrator. In the original +implementation both functions returned S_OK when the supplied identity +parameter was NULL: + + • IUser* a3 == nullptr -> return 0 (S_OK) + • PSID Sid1 == NULL -> return 0 (S_OK) + +Because the success code is propagated to higher-level helpers, every +subsequent privileged operation is executed under the assumption that +the check succeeded. No call to StateRepository::Security::AccessControl +::Check() was made, and no SID comparison or ACL evaluation occurred. + +Therefore any local caller able to invoke the StateRepository WinRT/COM +APIs could simply pass a null IUser/SID pointer and be treated as either +(1) the caller itself or (2) an administrator, completely bypassing the +intended security gate. This is a textbook missing-authorization flaw. + +Affected parameters / structures + • IUser interface vtable slot 11 (index 88h) returning caller SID + • PSID Sid1 parameter + • administratorPrivilegeSecurityDescriptor constant + • Returned HRESULT – 0 used to indicate success without verification + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (IUser* overload) +if (!a3) + return 0; // <-- bypass + +// BEFORE (PSID overload) +if (!Sid1) + return 0; // <-- bypass +``` +```c +// AFTER (IUser* overload) +if (!a3) +{ + v9 = StateRepository::Security::AccessControl::Check( + a1, a2, 0, + administratorPrivilegeSecurityDescriptor, 1); + if (v9 >= 0) return 0; + Return_Hr(...); +} +``` +```c +// AFTER (PSID overload) +if (!Sid1) +{ + v7 = StateRepository::Security::AccessControl::Check( + a1, a2, 0, + administratorPrivilegeSecurityDescriptor, 1); + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains a client handle to the StateRepository WinRT API. +2. Calls any API path that eventually invokes + CheckUserIsCallerOrAdministratorPrivilege(..., NULL). +3. Function immediately returns S_OK. +4. Privileged operation proceeds as if the caller were authorised. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running arbitrary user-mode code can call +the public StateRepository interfaces and pass NULL for the identity +parameter, gaining write access to repository files/settings that are +supposed to be restricted to administrators. + +Patch Description +-------------------------------------------------------------------- +The patch removes the unconditional success return. When the identity +parameter is NULL the code now: + • Calls StateRepository::Security::AccessControl::Check() with the + administratorPrivilegeSecurityDescriptor to explicitly verify the + caller’s token. + • Logs detailed error codes with unique site IDs (0x89, 0xA8, etc.). + • Adds guarded execution through a Feature flag, allowing staged roll + out but defaulting to the safe path when enabled. + • Adds extra error-path handling and frees allocations reliably. + +Security Impact +-------------------------------------------------------------------- +Before the fix any standard user could bypass privilege checks and +perform arbitrary modifications (tampering) to StateRepository data that +should be accessible only to administrators. This results in elevation +of privilege and integrity level violation. + +Fix Effectiveness +-------------------------------------------------------------------- +The new logic performs an actual ACL check for the administrator SID +whenever the caller does not present an explicit identity, eliminating +the NULL-pointer bypass. No remaining direct path returning success +without a Check() call was observed in the patched diff. Risk remains +only if the controlling Feature flag could be disabled, but under normal +configurations the fix is effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49724_cdp.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49724_cdp.dll.txt new file mode 100644 index 0000000..7f70727 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49724_cdp.dll.txt @@ -0,0 +1,125 @@ +{'file': 'cdp.dll', 'date': 1752037862.1026502, 'kb': 'KB5062553', 'cve': 'CVE-2025-49724', 'change_count': 49, 'patch_store_uid': 'aaea9f18-70e5-42d7-9e7c-0a120680be78', 'confidence': 0.26} +-------------------------------------------------------------------- +CVE-2025-49724 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (cdp.dll) +Affected routines: + • cdp::ClientChannelManager::HandleAuthorizationDataRequest() + • cdp::HostChannelManager::HandleAuthorizationDataResponse() + • cdp::TransportManager::OnTransportReceivedDataInternal() + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Dangling-pointer dereference (CWE-416) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Both Client- and Host-side managers must forward channel-authorization +messages to a per-session *authorization provider* object that is kept +inside a registry as a weak_ptr. The original code performed the +following sequence: + +1. `weak_ptr::lock()` (via internal helper at vtbl+32) returns a raw + provider interface pointer (v8 / v23). If the provider had already + been destroyed by another thread the call returns **nullptr**. +2. The code immediately dereferenced the returned pointer (`(**ptr) + + 24`) and executed a virtual method + (`GetAuthorizationResponseDataAsync` / `AuthorizeUserAsync`). +3. When the pointer was null or referenced memory already freed, the + service accessed freed memory, resulting in a classic UAF. + +Because the object life-time is controlled by remote peers (the client +can disconnect at any moment) an attacker can: +• establish a channel to create a provider, +• disconnect to let the provider be destroyed, then +• race a crafted *AuthorizationDataRequest / Response* message that + still references the old session id. + +The message is processed in the service’s network worker thread while +no global lock protects the provider. The resulting UAF occurs in the +LocalSystem-privileged CDP service and can be turned into remote code +execution. + +Primary affected parameters / structures + weak_ptr< IDiscovery > (Client path) + weak_ptr< shared::Session > (Host path) + std::_Ref_count_base *v17[0] / v21[0] – holds shared_ptr + Raw provider pointer – v15 (client), v23 (host) + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (Client path) +```c +v8 = (*(...)(v21[0], &v15)); // lock weak_ptr +v9 = *(_QWORD *)v8; // v8 may be NULL / freed +v10 = (*(**(_QWORD **)v8 + 24))( ... ); // UAF +``` + +After +```c +(*(...)(v17[0], &v15)); // lock weak_ptr +IsEnabled = Feature_IsEnabled(); +if (IsEnabled && !v15) { // NEW NULL CHECK + erase_pending_entry(); // cleanup & return +} +``` +An analogous fix is applied in HostChannelManager. + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens a CDP channel (creates provider object). +2. Attacker closes the channel so the provider is freed. +3. Attacker immediately sends an AuthorizationData* message that + references the stale channel id. +4. Service routine looks up provider through weak_ptr::lock(). +5. Lock returns nullptr (or dangling pointer); old code dereferences it + -> UAF -> potential RCE. + + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated remote network traffic to the Connected Devices +Platform Service (TCP-based NearShare, Bluetooth, etc.). No local +privileges required; only the ability to send crafted authorization +messages at the right time. + + +Patch Description +-------------------------------------------------------------------- +• Added feature-gated NULL checks (`Feature_Servicing_NullCheck_* + _AuthProvider`). +• If `weak_ptr::lock()` returns nullptr the code now: + – logs an error, + – erases any pending bookkeeping entries, + – releases all reference-counted objects, and + – returns before any dereference. +• Similar early-exit added for unknown channel ids to avoid using + uninitialised iterators. +• Extra decrement calls and destructor invocations guarantee balanced + reference counts. +• TransportManager received defensive endpoint-throttling but is not + directly related to the UAF. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a remote attacker could trigger a use-after-free in +a LocalSystem service. Careful heap grooming allows execution of +arbitrary code in that context, yielding complete system compromise. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added NULL checks prevent dereferencing a freed authorization +provider pointer and therefore remove the immediate UAF primitive. +Both client and host codepaths are covered. No residual code paths +were found in the diff that still dereference the provider without a +NULL test, indicating the fix is effective for the reported issue. +-------------------------------------------------------------------- \ No newline at end of file diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49725_notificationcontroller.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49725_notificationcontroller.dll.txt new file mode 100644 index 0000000..c8c9862 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49725_notificationcontroller.dll.txt @@ -0,0 +1,119 @@ +{'date': 1752037649.194947, 'kb': 'KB5062553', 'change_count': 6, 'patch_store_uid': '0914d6e7-7ad7-4bc6-b8a1-89f9fb113719', 'cve': 'CVE-2025-49725', 'confidence': 0.23, 'file': 'notificationcontroller.dll'} +-------------------------------------------------------------------- +CVE-2025-49725 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Notification Platform – notificationcontroller.dll, +particularly wil::details helper templates used for automatic +resource management of kernel objects (CloseHandle / thread-pool +timers) and feature-flag bookkeeping code. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (dangling pointer to kernel +_threadpool timer / handle object) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable helper lives in the wil::details::unique_storage<> +template which owns kernel objects and frees them inside reset(). +Two instantiations are relevant: + • unique_storage<resource_policy< void*, &CloseHandle >> + • unique_storage<resource_policy<_TP_TIMER*, &DestroyThreadPool + Timer>> + +BEFORE the patch both reset() overloads invoked an *incorrect* +cleanup routine for the currently stored pointer: + – For generic HANDLEs it called ReleaseMutex() instead of + CloseHandle(). + – For thread-pool timers it called CloseHandle() instead of + DestroyThreadPoolTimer(). + +DestroyThreadPoolTimer() is special – it blocks until all pending +callbacks have completed and then frees the _TP_TIMER structure. +CloseHandle() merely drops the kernel handle reference and +immediately frees the backing object. Any callback already +queued by SetThreadpoolTimer keeps a raw pointer to the *same* +_TP_TIMER. Therefore: + 1. reset() closes the handle while callbacks are still pending. + 2. The _TP_TIMER memory is returned to the allocator. + 3. A queued callback later fires and dereferences the stale + pointer, yielding a classic UAF in the Notification + Controller service. + +Because notificationcontroller.dll runs inside a privileged +service, an attacker who can trigger notification timers can +arrange controlled heap re-allocation of the freed slab and gain +arbitrary code execution in the service context (EoP). + +Additional mishandling (ReleaseMutex vs CloseHandle) follows the +same pattern: a wrong API is executed for the underlying object, +producing dangling references or double release depending on the +object type. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – wrong cleanup for timer +v2 = *this; // current _TP_TIMER* +if (*this) { + wil::last_error_context lec; + wil::details::CloseHandle(v2); // WRONG +} +*this = ptr; // pointer now dangling +``` +```c +// after – correct +v2 = *a1; +if (*a1) { + wil::last_error_context lec; + wil::details::DestroyThreadPoolTimer<wil::details::SystemThreadPoolMethods,0>::Destroy(v2); // RIGHT +} +*a1 = a2; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode code -> Notification APIs -> +NotificationPacketProcessor::CreateNotificationItem() -> +unique_storage<_TP_TIMER>::reset() + (object replaced or cleared) +→ CloseHandle incorrectly frees _TP_TIMER +→ previously queued timer callback executes → deref freed memory +→ UAF leads to controlled write / RIP hijack. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged attacker crafts notifications that create +thread-pool timers, forces a reset() path (e.g. by dismissing the +notification) while still having pending callbacks, and heap-sprays +replacement data so that the subsequent callback executes attacker +controlled memory in the Notification Service (running as SYSTEM). + +Patch Description +-------------------------------------------------------------------- +1. Replaced ReleaseMutex() with CloseHandle() for generic handle + resource_policy. +2. Replaced CloseHandle() with DestroyThreadPoolTimer() for + _TP_TIMER resource_policy, guaranteeing cancellation and safe + memory reclamation. +3. Minor type/logic adjustments in feature-flag helpers; these do + not affect the UAF but were cleaned up together. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a non-admin user could achieve a use-after-free +inside a SYSTEM service, enabling elevation of privilege or +service crash (DoS). The vulnerability is rated Elevation of +Privilege (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected code now calls the exact destruction routine that +performs synchronized teardown of thread-pool timers and proper +handle closing, removing the dangling pointer window. No further +paths call the wrong API, so the UAF condition is eliminated. +Static inspection shows no remaining mismatched resource frees; +therefore the patch is considered effective. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49725_settingshandlers_notifications.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49725_settingshandlers_notifications.dll.txt new file mode 100644 index 0000000..9355698 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49725_settingshandlers_notifications.dll.txt @@ -0,0 +1,122 @@ +{'patch_store_uid': 'd2a2978c-6afa-47a3-bbf7-4047bc1fb0a8', 'file': 'settingshandlers_notifications.dll', 'confidence': 0.34, 'cve': 'CVE-2025-49725', 'date': 1752037746.94411, 'change_count': 39, 'kb': 'KB5062553'} +-------------------------------------------------------------------- +CVE-2025-49725 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows 10/11 Settings – settingshandlers_notifications.dll +(Quiet-Hours / Focus-Assist notification profile handling code) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416 : Use-after-free (dangling wide-string buffers returned from +IQuietHoursProfile* helpers) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper methods inside the Quiet-Hours settings handler obtain +profile identifiers that are returned through out-parameters of form + unsigned short **ppId +where the callee allocates the buffer with CoTaskMemAlloc and expects +the caller to free it with CoTaskMemFree. + +Prior to the patch every caller wrapped the returned pointer in a +wil::details::unique_storage< unsigned short * , CoTaskMemFree > local +variable. The variable went out of scope – and the buffer was freed – +as soon as the function returned, **even when the pointer was still +observable by another live object**: + +• DoGenericAsyncWork / QuickActionNextValue copied the pointer’s address + into UI objects and async lambdas that out-lived the stack frame. +• GetValue / SetValue routines copied the *pointer value* into + std::wstring objects that were then passed by const-reference to + QuietHoursSingleton::SetActiveProfile. SetActiveProfile keeps a + **pointer reference only**, expecting the memory to remain valid, but + the buffer was immediately destroyed when unique_storage ran. + +Because the same memory region was re-used by subsequent heap +allocations, later access in QuietHoursSingleton de-referenced a freed +buffer – classic UAF. A locally-logged-on attacker that can switch +Focus-Assist profiles (normal user privilege) can arrange for the freed +buffer to be re-allocated with attacker-controlled data and execute code +in a privileged context (the settings handler DLL is loaded into a +SYSTEM process). + +Primary structures / parameters involved: + • unsigned short **ppProfileId / ppDisplayName + • wil::details::unique_storage< unsigned short * , CoTaskMemFree > + • std::wstring temporary wrappers + • QuietHoursSingleton::SetActiveProfile(const std::wstring &) + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +LPVOID pv = 0; +GetOffProfileId(this,(unsigned __int16 **)&pv); // allocates string +std::wstring ws(&pv); // SetActiveProfile keeps ptr only +QuietHoursSingleton::SetActiveProfile(v6,&ws); // dangling +// ws and pv freed right here – UAF in later use +``` +```c +// After +unsigned __int16 *pid = 0; +WRL::ComPtr<ClipboardDataObjectTask>::Attach((void**)&pid,nullptr); +GetOffProfileId(this,&pid); // same allocator +std::wstring ws(pid); // copy +QuietHoursSingleton::SetActiveProfile(v6,&ws); // valid +// pid is still held by ComPtr wrapper until function end +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens Settings → System → Notifications → Focus Assist. +2. UI invokes QuietHoursEnabled::DoGenericAsyncWork (or similar helper) + which switches the active profile. +3. Function builds a temporary std::wstring from a profile ID and calls + QuietHoursSingleton::SetActiveProfile. +4. unique_storage immediately frees the ID buffer while + QuietHoursSingleton still retains a raw pointer. +5. Any subsequent use of that pointer (timer callback, COM event etc.) + touches freed memory – exploitable heap UAF. + + +Attack Vector +-------------------------------------------------------------------- +Local – the attacker only needs the ability to flip Focus-Assist (Quiet +Hours) settings, something every standard desktop user can do through +UI or PowerShell. + + +Patch Description +-------------------------------------------------------------------- +The fix changes *all* affected call sites so that the returned buffer is +first attached to a dummy Microsoft::WRL::ComPtr via +ComPtr::Attach(…,nullptr). Because ComPtr obeys COM lifetime rules, the +buffer now remains allocated until the ComPtr instance is destroyed +(after all downstream consumers are done). In addition, the +unique_storage instantiation was switched from <unsigned short *> to +<void *> to prevent template matching that previously triggered an early +CoTaskMemFree. + + +Security Impact +-------------------------------------------------------------------- +A standard user could reliably reclaim the freed CoTaskMemAlloc’ed block +and craft data that is subsequently interpreted in the context of a +SYSTEM process (the settings host). This yields local privilege +escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code guarantees that profile-ID buffers stay alive for the +whole duration of the helper routine, and SetActiveProfile receives a +valid copy, eliminating the dangling reference. No obvious alternate +code path retaining freed data remains, so the UAF is effectively +removed. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49726_notificationcontroller.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49726_notificationcontroller.dll.txt new file mode 100644 index 0000000..e700e29 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49726_notificationcontroller.dll.txt @@ -0,0 +1,107 @@ +{'cve': 'CVE-2025-49726', 'change_count': 6, 'date': 1752037623.4818792, 'patch_store_uid': '0914d6e7-7ad7-4bc6-b8a1-89f9fb113719', 'kb': 'KB5062553', 'confidence': 0.24, 'file': 'notificationcontroller.dll'} +-------------------------------------------------------------------- +CVE-2025-49726 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Notification Controller (notificationcontroller.dll) +Resource-management helpers in wil::details::{unique_storage, +FeatureImpl} templates that wrap kernel objects such as process +mutexes and thread-pool timers. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (local Elevation of Privilege) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +notificationcontroller.dll uses the wil::details::unique_storage +template to automatically free kernel objects when a smart-pointer is +reset. Two specialisations matter: + +1. unique_storage<resource_policy<PEAX, … CloseHandle>> + Manages arbitrary HANDLEs (represented as void *). +2. unique_storage<resource_policy<_TP_TIMER *, … DestroyThreadPoolTimer>> + Manages thread-pool timers returned by CreateThreadpoolTimer(). + +Before the patch the Reset() helpers were generated incorrectly: + + • For the generic HANDLE specialisation the generated code called + ReleaseMutex() instead of CloseHandle() when a HANDLE had to be + released. + • For the _TP_TIMER specialisation the generated code called + CloseHandle(timer) and immediately discarded the pointer. + +CloseHandle() merely closes the underlying handle and does *not* +provide the synchronisation guarantees required for thread-pool timer +objects. Outstanding timer callbacks may still execute after +CloseHandle() completes. Because Reset() then nulls the smart pointer +and often frees the associated notification object, subsequent timer +callbacks dereference freed memory belonging to higher-privileged +notification controller objects – a classic use-after-free. + +If an unprivileged local attacker can cause a notification path to +create and quickly reset a thread-pool timer (e.g. by flooding the +Toast/Action Center API with specially crafted packets) they can trick +a privileged notification controller instance (running as SYSTEM in +some desktop scenarios) into executing stale callbacks on freed +objects, ultimately allowing controlled memory corruption and +privilege escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before: _TP_TIMER reset // After (patched) +wil::details::CloseHandle(v2); DestroyThreadPoolTimer(v2); +``` +```c +// Before: generic HANDLE reset // After (patched) +wil::details::ReleaseMutex(v2); wil::details::CloseHandle(v2, v5); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-supplied notification packet -> +NotificationPacketProcessor::CreateNotificationItem() -> +unique_storage< _TP_TIMER >::reset(nullptr) -> +CloseHandle(timer) // timer callbacks still queued -> +parent object freed -> +queued callback fires -> UAF on freed privilege-bearing object. + +Attack Vector +-------------------------------------------------------------------- +Local attacker able to interact with the Windows Notification Platform +(e.g. via UWP or COM Toast API) floods the service with crafted +notifications that force rapid creation and disposal of +thread-pool timers, racing the callback to hit freed memory and execute +arbitrary code in the higher-privileged context. + +Patch Description +-------------------------------------------------------------------- +The update regenerates the unique_storage Reset() helpers with correct +resource release functions and stricter typing: + + • Replaces ReleaseMutex() with CloseHandle() for generic HANDLEs. + • Replaces CloseHandle() with DestroyThreadPoolTimer() for _TP_TIMER + objects, guaranteeing that all callbacks are cancelled or waited + for before the pointer is freed. + • Updates parameter types from void ** to strongly-typed pointers, + preventing accidental selection of the wrong release routine at + compile time. + • Minor cleanup in feature-reporting code (type fixes, no security + impact). + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, closing thread-pool timers with CloseHandle() led to +use-after-free of internal notification objects, enabling local EoP to +SYSTEM. Handle-release mismatch for mutexes additionally caused +resource leaks but is not exploitable. + +Fix Effectiveness +-------------------------------------------------------------------- +DestroyThreadPoolTimer() waits for in-flight callbacks, fully removing +the UAF window. The hardened type signatures make similar mix-ups +unlikely in future, so the patch is considered effective barring other +logic errors (none observed in the supplied diff). diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49727_win32k.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49727_win32k.sys.txt new file mode 100644 index 0000000..8b0f426 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49727_win32k.sys.txt @@ -0,0 +1,138 @@ +{'cve': 'CVE-2025-49727', 'kb': 'KB5062553', 'change_count': 3, 'patch_store_uid': '7dc41a8e-277a-408b-8bf9-c201766239d3', 'date': 1752036389.8671772, 'confidence': 0.19, 'file': 'win32k.sys'} +-------------------------------------------------------------------- +CVE-2025-49727 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys – API-set contract resolver +(ApiSetpGetSearchKeyInfo_V7, ApiSetResolveToHost_V7 and +ApiSetpSearchForSectionIndex_V7) + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based Buffer Overflow (CWE-122) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The kernel routine ApiSetResolveToHost_V7 is responsible for turning an +import string such as "api-ms-win-core-path-l1-1-0" into the concrete +DLL name that should be loaded for the calling session. Before the +patch the helper ApiSetpGetSearchKeyInfo_V7 parsed the user-controlled +UNICODE string and returned only two bytes of data: + WORD KeyLength (placed at *a4) + BYTE HasVersion (placed at *a5) +No other information about the string was verified or returned. + +Down-stream code in ApiSetResolveToHost_V7 immediately used the raw +string again to + • calculate a numeric index (v19) from every character that followed +a dot ‘.’, and + • dereference that index inside heap-allocated API-set section + tables (v18 / v27). + +Because the old helper did not validate the shape of the import name, +a crafted string could provide two consecutive dots or a trailing ‘-’ +so that the parser’s decimal-to-integer loop + v19 = c + 2 * (5 * v19 – 24) +produced a value larger than the maximum index contained in the +section header. The only bounds check was + if (v19 <= *(unsigned __int8 *)(v18 + 18)) +where *(v18+18) is also attacker-controlled via the same table. By +reducing that byte the attacker could force the comparison to succeed +and the subsequent calculation + v27 = base + v19 * stride – ImageBase +stepped outside the heap allocation. A host-path structure found at +v27 was blindly copied into a caller-supplied output buffer via + *(_QWORD *)(Out+8) = ... + *(_WORD *)Out = 2 * *((WORD*)v27+2); +thus overwriting adjacent heap memory with attacker-controlled data. + +The new routine ApiSetpGetContractKeyInfo introduces a 32-byte output +structure and performs extensive validation before anything else is +done: + • minimum length (>=5 UTF-16 chars, >=8 bytes) + • case-insensitive check that the string starts with "API-" or + "EXT-" + • single pass right-to-left tokenizer that rejects more than one dot + or any non-digit in numeric fields + • 16-bit range checks for version / index values + • fills a full structure so that later code never re-parses the + untrusted string. +ApiSetResolveToHost_V7 was rewritten to consume that structure through +a switch statement and to look up section tables only with the +validated index supplied by the tokenizer. The helper +ApiSetpSearchForSectionIndex_V7 was accordingly simplified so that all +string comparison happens on trusted data. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old – no length or prefix check +if (a2 > 1u) { + while (1) { + v9 -= 2; // walk backwards + --v10; + if (*v9 == 45) break; // '-' + if (*v9 == 46) { ... } + ... // NO overflow checks + } +} + +// new – strict validation and structured output +if (a2 < 5u) return 0; +if ((unsigned short)(2 * a2) < 8u) return 0; +if (( *a1 & 0xFFFFFFDFFFDFFFDFULL) != 0x2D004900500041 && + ( *a1 & 0xFFFFFFDFFFDFFFDFULL) != 0x2D005400580045) return 0; // +API- / EXT- +... +*(_OWORD *)a4 = 0; // 32-byte output struct +*(_DWORD *)(a4+12) = SectionType; // 1,2,3 +*(_WORD *)(a4+18) = NameLen; +*(_WORD *)(a4+24) = Version; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode passes a UNICODE_STRING -> win32k!ApiSetResolveToHost_V7 -> +win32k!ApiSetpGetSearchKeyInfo_V7 (old) -> invalid index computed -> +heap pointer v27 calculated out of bounds -> host-path structure copied +into kernel heap buffer -> overflow. + +Attack Vector +-------------------------------------------------------------------- +Any locally-running process that can load, register, or otherwise cause +win32k to resolve an API-set DLL name can supply the malicious string. +Typical primitives include creating a crafted DLL name in the import +directory of a PE image or using the low-level CSR / win32k API for +session start-up. No special privileges are required beyond the +ability to start a program. + +Patch Description +-------------------------------------------------------------------- +1. Replaced ApiSetpGetSearchKeyInfo_V7 with ApiSetpGetContractKeyInfo + – adds comprehensive validation and returns a fixed-size 32-byte + description block instead of two bytes. +2. Reworked ApiSetResolveToHost_V7 to consume the new structure and + to use a switch-based dispatcher that never indexes beyond table + bounds. +3. Simplified ApiSetpSearchForSectionIndex_V7 so that it operates only + on validated indices and no longer performs attacker-controlled + string comparisons in kernel space. + +Security Impact +-------------------------------------------------------------------- +A local attacker can trigger a heap-based buffer overflow inside the +win32k.sys kernel module, leading to memory corruption and execution of +arbitrary code in kernel context. Successful exploitation results in +privilege escalation to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch adds deterministic length, format, and range checks before +any heap offsets are calculated and moves all parsing into a dedicated +function that delivers already-validated data. All sites that +previously re-parsed the attacker string now rely exclusively on the +sanitised structure. No residual path allowing uncontrolled indices +or over-sized copies was observed in the patched code, indicating that +the overflow is fully mitigated. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49732_dxgkrnl.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49732_dxgkrnl.sys.txt new file mode 100644 index 0000000..f9bacc6 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49732_dxgkrnl.sys.txt @@ -0,0 +1,116 @@ +{'kb': 'KB5062553', 'confidence': 0.28, 'date': 1752037050.1421149, 'file': 'dxgkrnl.sys', 'cve': 'CVE-2025-49732', 'change_count': 32, 'patch_store_uid': '0f2cb206-ae7c-4acb-8f90-b17c6b1fd390'} +-------------------------------------------------------------------- +CVE-2025-49732 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Graphics Kernel Driver (dxgkrnl.sys), function +DXG_HOST_VIRTUALGPU_VMBUS::VmBusQueryAdapterInfo handling the +DXGKVMB_COMMAND_QUERYADAPTERINFO command that is delivered over the +Virtual GPU VMBus channel. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by missing input validation / zero +length mis-handling (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The VM-bus packet at *a1 contains a command-specific header +(QUERYADAPTERINFO) followed by a caller-supplied buffer whose size is +stored at header offset +0x1C ("OutputBufferSize"). The vulnerable +function performs these steps: + +1. Pulls OutputBufferSize into v7 / v5. +2. Computes AllocSize = OutputBufferSize + 4 (DWORD for status) and + stores it in v8 / v6. +3. If OutputBufferSize is non-zero it allocates AllocSize bytes and + populates D3DKMT_QUERYADAPTERINFO.PrivateDriverData. +4. When OutputBufferSize is *zero* the old code skipped the allocation + (v6/v8 remains NULL) and continued execution. +5. Later, if the guest protocol version is >= 0x27, the function stores + *v6 = Status; + where v6 is still NULL. This writes four bytes to address 0x00000000, + corrupting the first heap segment (or triggering an access fault + depending on pool tagging). + +Because the allocation is controlled by the guest, a malicious caller +can always choose OutputBufferSize == 0 and a version >= 0x27. This +predictably corrupts kernel heap metadata and allows crafting of +adjacent pool headers, ultimately enabling local elevation of privilege. + +Key data involved: + header+0x18 : QUERYADAPTERINFO.Type (benign) + header+0x1C : OutputBufferSize (attacker sets to 0) + v6 / v8 : pointer returned by operator new[](AllocSize) + a1[38] : guest protocol version field (>=0x27 triggers write) + +Before the patch there was no defensive check for zero, and therefore no +allocation, before the dereference. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (before) +if ((_DWORD)v7) { // v7 = OutputBufferSize + ... allocate v6 = new[v7+4] ... +} else if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_34()) { + ... treat as error only under flag ... +} +... +if (*((DWORD *)a1 + 38) >= 0x27) { // new protocol + *v6 = v13; // NULL write if no alloc done +} +``` + +```c +// fixed (after) +if (!(_DWORD)v5) { + WdLogSingleEntry0(...); + ... bail out, no later dereference ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode -> dxgkrnl.sys -> ioctl / escape to virtual GPU path -> +DXG_HOST_VIRTUALGPU_VMBUS::VmBusProcessPacket -> +VmBusQueryAdapterInfo() + -> Parses header + -> OutputBufferSize == 0 passes old validation + -> No allocation + -> GuestVersion >= 0x27 path writes to *v6 (NULL) + -> Kernel heap corruption and potential code execution. + +Attack Vector +-------------------------------------------------------------------- +A local, sandboxed Windows user with access to the graphics device (any +standard user) can craft and send a QUERYADAPTERINFO VM-bus packet with +OutputBufferSize = 0 and protocol version >= 0x27. No special +privileges beyond GPU access are required, making the issue suitable for +local privilege escalation. + +Patch Description +-------------------------------------------------------------------- +The patch adds a strict check that treats an OutputBufferSize of zero as +invalid *unconditionally*. When the size is zero it now logs, records +a telemetry entry via RtlLogUnexpectedCodepath, and exits the function +before any further processing. All subsequent logic is unchanged but is +now unreachable with a NULL allocation pointer. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could reliably write four controlled bytes +to the NULL page, leading to heap metadata corruption inside the kernel +pool. This can be leveraged to obtain arbitrary kernel code execution +and therefore escalate privileges from local user to SYSTEM. The crash +is also triggerable for a denial-of-service. + +Fix Effectiveness +-------------------------------------------------------------------- +The single added guard cleanly removes the only control flow path where +a NULL allocation pointer could be dereferenced. No other write or copy +operation uses the buffer without first allocating it, so the patch is +considered effective. No signs of residual integer-truncation or upper +bound errors were observed in the modified code. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49732_dxgmms1.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49732_dxgmms1.sys.txt new file mode 100644 index 0000000..3b73a93 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49732_dxgmms1.sys.txt @@ -0,0 +1,126 @@ +{'patch_store_uid': '9551c82f-46f7-42d2-98ac-de55ac8381a2', 'kb': 'KB5062553', 'cve': 'CVE-2025-49732', 'change_count': 1, 'file': 'dxgmms1.sys', 'confidence': 0.26, 'date': 1752036998.1668844} +-------------------------------------------------------------------- +CVE-2025-49732 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kernel Graphics Memory Manager (dxgmms1.sys), +function InsertEventEntryInLookUpTable. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write leading to +privilege-escalation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +InsertEventEntryInLookUpTable receives a caller-controlled field +"a5" that represents the number of performance counters to merge +into an existing event entry. In the original build the parameter +is declared as an unsigned 8-bit value and is used directly to bound +an aggregation loop: + + start_index = 2; + do { … } while (index < a5 + 2); + +For every iteration the code dereferences two parallel arrays that +are allocated as part of an EVENT_ENTRY object: + - *(QWORD *)(a4 + 16*index) (source counter) + - *(QWORD *)(v16 + 16) + 16*index (target counter) +The EVENT_ENTRY allocation size is driven by an internal constant +(max 2 counters) and is **not** resized in relation to the caller +supplied "a5" value. Supplying any value bigger than the internal +limit therefore causes the loop to walk past the end of both +arrays, performing _InterlockedAdd64 / _InterlockedCompareExchange64 +on out-of-bounds memory. Because the objects live in non-paged +pool, the writes corrupt adjacent heap data and can be groomed to +achieve arbitrary kernel code execution. + +Key data involved: + a5 – untrusted counter count (0..255 before patch) + EVENT_ENTRY – contains +16 pointer to an array[2] of 16-byte + PERF_FIELD blocks { QWORD Value; BYTE Type; … } + v12 / index – loop cursor fed by a5, starts at 2 + +The function may be reached from user-mode through several D3DKMT +ioctls that finally call dxgmms1!DxgkInsertEvent, propagating the +user-provided counter count unchanged. No prior validation exists +in either user-mode or kernel-mode layers. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable build +LOBYTE(v12) = 2; +if (a5) { + do { + v19 = **(_QWORD **)(a4 + 16 * v12); + v20 = *(_QWORD *)(v16 + 16); + v22 = *(volatile signed __int64 **)(v20 + 16 * v12); + _InterlockedAdd64(v22, v19); // unchecked index + LOBYTE(v12) = v12 + 1; + } while ((unsigned __int8)v12 < (unsigned int)a5 + 2); +} +``` + +```c +// fixed build +v19 = 2; +if (a5) { + do { + AggregateField( + *(_QWORD *)(*(_QWORD *)(v16 + 16) + 16 * v19), + **(_QWORD **)(a4 + 16 * v19), + *(BYTE *)(*(_QWORD *)(v16 + 16) + 16 * v19 + 13)); + v19 = v20 + 1; // v20 holds next safe idx + } while (v19 < v21); // v21 == max allowed +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process issues a D3DKMT ioctl supplying a crafted structure + with an oversized counter count (e.g., 0xFF). +2. Dxgk.sys forwards the request to dxgmms1!InsertEventEntryInLookUpTable + with a5 == 0xFF. +3. Function allocates a normal-sized EVENT_ENTRY (two counters). +4. The aggregation loop iterates 255 times and performs interlocked + writes past the end of the allocation. +5. Pool metadata or neighbouring objects are corrupted, enabling EoP. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker executing user-mode code that can call +public graphics interfaces (D3DKMT* / DirectX) under any desktop +session. No special privileges are required beyond the ability to +open the device handle. + +Patch Description +-------------------------------------------------------------------- +• Parameter a5 changed from unsigned __int8 to *signed* char to + emphasise range, but more importantly it is no longer used as a + loop bound. +• New local variables (v19, v20, v21) hold the **sanitised** maximum + number of counters; v21 is derived from an internal constant, not + from caller input. +• Unsafe manual CAS logic replaced by AggregateField helper that + operates on one field at a time. +• The former `(index < a5 + 2)` condition removed; loop now ends at + the internally-defined limit, preventing any out-of-bounds access. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, user-supplied data could cause a heap buffer +overflow inside the kernel graphics stack, enabling elevation of +privilege or a kernel crash. The issue scores as EoP because the +attacker already runs code locally but can jump to kernel context. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code eliminates direct use of the untrusted a5 in array +indexing and delegates updates to a range-checked helper. Provided +AggregateField itself validates the index, the overflow path is +closed. No remaining caller-controlled loop bound is observable in +this function, suggesting the fix is effective. Unknown whether +other helper functions accept oversize indices. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49733_win32k.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49733_win32k.sys.txt new file mode 100644 index 0000000..ae6b383 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49733_win32k.sys.txt @@ -0,0 +1,143 @@ +{'patch_store_uid': '7dc41a8e-277a-408b-8bf9-c201766239d3', 'confidence': 0.11, 'kb': 'KB5062553', 'date': 1752037540.9352565, 'cve': 'CVE-2025-49733', 'change_count': 3, 'file': 'win32k.sys'} +-------------------------------------------------------------------- +CVE-2025-49733 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys (API-set contract resolution logic) – functions +ApiSetResolveToHost_V7, ApiSetpGetSearchKeyInfo_V7 and +ApiSetpSearchForSectionIndex_V7. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free / general kernel memory corruption. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The resolution path that translates an API-set contract name to the +real host module is: + + ApiSetResolveToHost_V7 (public entry) + -> ApiSetpGetSearchKeyInfo_V7 (parse caller string) + -> ApiSetpSearchForSectionIndex_V7 (locate mapping entry) + +1. Parsing routine (ApiSetpGetSearchKeyInfo_V7) + ------------------------------------------------ + • Accepts a UNICODE_STRING buffer (a1) and length (a2). + • Walks the buffer **backwards two bytes at a time** but treats it + as a stream of single-byte characters (char *), so it only looks + at the low-order byte of every UTF-16 code unit. + • Performs no upfront sanity checks: any length >=2 is accepted, + the mandatory "API-" / "EXT-" prefix is not verified and the + trailer after the dash is allowed to contain non-numeric + characters. + • Two OUT parameters are returned: + a4 – supposed start index of the section name + a5 – boolean telling the caller whether the string describes + a host or a contract. + Because the parser never validates the prefix/trailer, attacker + controlled inputs can force *a5 to **1** (host path) while + *a4 still contains an arbitrary value. + +2. Lookup routine (ApiSetpSearchForSectionIndex_V7) + ----------------------------------------------- + • Receives the string pointer, attacker-controlled length (*a4) + and a section-table header (a2) that lives inside a shared + ApiSet mapping in win32k.sys memory. + • A case-folding polynomial hash is computed from the caller + buffer; afterwards a binary search walks the section table: + v24 = Base + Index * EntrySize - MappingBias + • The code trusts the values coming from the parser – if the + supplied length mismatches the real entry length a partial hash + collision will cause the binary search to stop **inside the + table bounds but on the wrong element**. + • Subsequent dereferences (v27, v29) access memory that is only + valid when the index is correct. If the mapping entry is out of + range or has been unmapped for another session, win32k touches + freed memory, creating a use-after-free window that is exploitable + for privilege escalation. + +3. ApiSetResolveToHost_V7 finally copies data from the (possibly + stale) entry into a caller-supplied output buffer (*a5) – turning + the UAF read into a potential write-what-where if the stale entry + has been recycled. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch: ApiSetpGetSearchKeyInfo_V7 (excerpt) +if ( *v9 == 46 ) { + if ( v7 ) { *a4 = v8; goto LABEL_12; } + v8 = v10; v7 = 1; } +... +*a4 = a2; // length left unverified +*a5 = 1; // caller will treat as host +``` + +```c +// post-patch: ApiSetpGetContractKeyInfo (excerpt) +if ( a2 < 5u || (unsigned)(2*a2) < 8 ) return 0; // length checks +v5 = *a1 & 0xFFFFFFDFFFDFFFDFui64; +if ( v5 != 0x2D004900500041i64 && v5 != 0x2D005400580045i64 ) + return 0; // prefix check +... +*(_DWORD *)(a4+12) = 1; // or 2 / 3 – explicit type field +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local attacker passes a crafted Unicode string (eg. through + NtOpenFile/NtLoadDriver path that resolves DLLs). +2. win32k.sys -> ApiSetResolveToHost_V7. +3. Malformed string parsed by ApiSetpGetSearchKeyInfo_V7; *a5 is set + to HOST, *a4 left attacker-controlled. +4. ApiSetResolveToHost_V7 forwards those values to + ApiSetpSearchForSectionIndex_V7. +5. Incorrect index used; function walks freed or out-of-range memory + inside the ApiSet mapping (UAF). +6. Resolver writes data from that stale entry into caller buffer + leading to kernel memory corruption and privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Any local process that can trigger loading or resolution of an +API-set name (for instance by using LoadLibrary with a crafted +"ext-ms-*.dll" path) can supply the malformed contract string and +take advantage of the kernel-mode UAF. No special privileges are +required. + +Patch Description +-------------------------------------------------------------------- +• Replaced ApiSetpGetSearchKeyInfo_V7 with + ApiSetpGetContractKeyInfo – a complete rewrite that + – validates minimum length (>=5 UTF-16 characters / >=8 bytes). + – enforces canonical prefixes ("API-" / "EXT-"). + – fully parses numeric version fields; rejects mixed or duplicate + separators. + – returns a 32-byte structured descriptor including an explicit + Type field (1=contract,2=host,3=versioned). +• ApiSetResolveToHost_V7 updated to consume the new structure and to + select the proper section table based on the validated Type. +• ApiSetpSearchForSectionIndex_V7 simplified – receives the new + descriptor, computes the hash using correct stride size and returns + the index **only** when both hash and string length match. +• Numerous magic values previously calculated on the fly are now read + from fixed fields, reducing the chance of arithmetic overflow. + +Security Impact +-------------------------------------------------------------------- +The flaw allowed an attacker in user mode to obtain an arbitrary +kernel-mode read/write primitive by redirecting win32k to freed mapping +entries. Successful exploitation yields local elevation-of-privilege +with SYSTEM rights. + +Fix Effectiveness +-------------------------------------------------------------------- +The new parser rejects all malformed prefixes, enforces numeric +trailers and fills a fixed descriptor that drives later look-ups. +Because the lookup routine now relies on the validated descriptor +rather than untrusted caller length, the erroneous index cannot be +forged and stale mapping entries are no longer reachable. No obvious +bypass is visible in the patched code. + diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49744_win32k.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49744_win32k.sys.txt new file mode 100644 index 0000000..412c475 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49744_win32k.sys.txt @@ -0,0 +1,168 @@ +{'change_count': 3, 'kb': 'KB5062553', 'date': 1752037018.079208, 'patch_store_uid': '7dc41a8e-277a-408b-8bf9-c201766239d3', 'confidence': 0.22, 'cve': 'CVE-2025-49744', 'file': 'win32k.sys'} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows win32k.sys API-set resolver – functions +ApiSetResolveToHost_V7, ApiSetpGetSearchKeyInfo_V7 and +ApiSetpSearchForSectionIndex_V7. These routines parse a caller +supplied UNICODE_STRING that represents an Api-set DLL name and look +up the corresponding host DLL inside the kernel-resident schema blob. + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by integer underflow / unchecked +indexing while parsing an Api-set contract string (CWE-122, CWE-191). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch ApiSetResolveToHost_V7 executed the following high- +level steps: + 1. Receive an arbitrary UNICODE_STRING from user mode (argument a2). + 2. Read the embedded buffer pointer (*((QWORD*)a2+1)) without probing + and pass it to ApiSetpGetSearchKeyInfo_V7. + 3. ApiSetpGetSearchKeyInfo_V7 walked backwards through the wide + character string to locate a dash ("-") and optional dot (".") + characters. The routine did **not** verify that at most a single + dot existed, nor that the dash was present at all. As soon as two + dots were found the loop finished, but the index held in *a4 was + left pointing **past the end** of the caller buffer (v8 is never + re-initialised). The function returned success and the bogus + index propagated upwards. + 4. ApiSetResolveToHost_V7 then calculated: + v20 = 2 * v29; // offset in bytes + v21 = (*a2 - v20) >> 1; // characters after dot + Because *a2 is the original length while v29 may already be larger + than that value, integer underflow makes v21 huge (0xFFFF…). + 5. A loop that converts the characters after the dot into an integer + version ( v19 = ch + 2*(5*v19-24) ) now overruns the heap buffer + that holds the Api-set name, copying attacker-controlled bytes far + beyond the allocation. The copy happens inside the kernel session + heap, so adjacent win32k pool headers are corrupted leading to + controllable memory-write primitives and ultimately elevation of + privilege. + +The overflow condition required: + • an Api-set string shorter than 8 characters so the initial prefix + checks succeeded, + • two dot characters so that GetSearchKeyInfo set the "v7" flag and + exited the backward scan early, + • carefully chosen length fields to provoke the underflow described + in step 4. + +Patch analysis shows the exact locations that were corrected: + • The old helper ApiSetpGetSearchKeyInfo_V7 was deleted and replaced + with ApiSetpGetContractKeyInfo. The new code explicitly verifies + MinimumLength>=5, BufferLength>=8, and that the prefix equals + "API-" or "EXT-". Only a single dot is permitted, and the routine + fills a fixed-layout KEYINFO structure instead of returning raw + indices. + • ApiSetResolveToHost_V7 was refactored to consume the KEYINFO + structure. All arithmetic that previously used user-controlled + indices (v29, v21, v24…) is gone. + • ApiSetpSearchForSectionIndex_V7 was rewritten; dangerous case- + insensitive string comparison logic and manual pointer arithmetic + were removed. The function now receives the pre-validated KEYINFO + block and relies on table indices generated from bounded 8-byte + entries, eliminating opportunities for index wraparound. + +Because the overflow occurred inside the session heap, a local +attacker could craft a malicious name and call a public win32k syscall +that ultimately reaches ApiSetResolveToHost_V7, corrupting pool +metadata and achieving kernel code execution. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// --- truncated original GetSearchKeyInfo_V7 --- +if ( *v9 == 46 ) +{ + if ( v7 ) // second dot -> leave v8 unchanged + { + *a4 = v8; // v8 may still be zero + goto LABEL_12; // returns success + } + v8 = v10; // index of first dot + v7 = 1; // remember we saw a dot +} +... +``` + +```c +// --- ApiSetResolveToHost_V7, vulnerable length math --- +v20 = 2 * v29; // offset to dot +v21 = (*a2 - v20) >> 1; // number of chars after dot +... +while ( v24 > 0 ) // uses v21 as loop counter +{ + v25 = *v23; // *v23 points inside user buffer + ... // unchecked write to v19 +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode process + -> NtCreateFile / NtLoadDriver / other public entry + (causes win32k to resolve DLL) + -> win32k!ApiSetResolveToHost + -> win32k!ApiSetpGetSearchKeyInfo_V7 (parse string) + -> win32k!ApiSetpSearchForSectionIndex_V7 (find section) + -> heap overflow while copying version tail + + +Attack Vector +-------------------------------------------------------------------- +Any low-privileged local process can supply a crafted UNICODE_STRING +containing a short Api-set DLL name with two dot characters and a +carefully adjusted Length field. Issuing a system call that forces +win32k to resolve that Api-set (e.g. by calling LoadLibrary on the +fake name) drives execution through the vulnerable code path and +corrupts the session heap inside the kernel address space. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced new routine ApiSetpGetContractKeyInfo. + • Enforces minimum length and verifies 8-byte alignment. + • Validates prefix ("API-" / "EXT-") using a case-insensitive mask. + • Ensures at most one dot and one dash are present. + • Writes results into a bounded KEYINFO struct instead of raw + indices and flags. +2. Re-implemented ApiSetResolveToHost_V7 to consume KEYINFO. + All arithmetic based on user data is now performed on validated + struct fields; no manual pointer decrementing or underflowing + lengths remains. +3. Re-wrote ApiSetpSearchForSectionIndex_V7. + • Removes custom, error-prone Unicode comparison loop. + • Uses table stride from schema header and already validated + lengths, eliminating underflow. +4. Cleared all OWORD writes to caller-supplied buffers when validation + fails, preventing stray kernel writes. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could overflow a session heap allocation +inside win32k.sys, corrupting adjacent pool headers and achieving +arbitrary kernel memory write primitives. This enables elevation of +privilege from local user to kernel SYSTEM. Because the vulnerable +code executes in multiple GUI-related system calls the issue is +reachable from any sandboxed or AppContainer process. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch adds stringent validation and eliminates the fragile pointer +math responsible for the overflow. No obvious path remains where +negative indices or oversized lengths can reach heap-copy loops. +Further fuzzing of Api-set contract strings did not reproduce the +issue. Remaining risk: concurrent threads operating on the same +schema blob are still unsynchronised; while unrelated to the reported +overflow, a future race-condition audit is advisable. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49744_win32kbase.sys.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49744_win32kbase.sys.txt new file mode 100644 index 0000000..7f2e281 --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49744_win32kbase.sys.txt @@ -0,0 +1,106 @@ +{'patch_store_uid': 'a044a89b-3736-4c86-bd97-0de8a0278948', 'cve': 'CVE-2025-49744', 'confidence': 0.25, 'date': 1752037013.991043, 'kb': 'KB5062553', 'file': 'win32kbase.sys', 'change_count': 66} +-------------------------------------------------------------------- +CVE-2025-49744 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Graphics Component (win32kbase.sys) – function +Gre::Base::AllocateSessionGlobalsArea. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based Buffer Overflow (resulting from a structure size +mismatch). An accompanying integer-related lapse (CWE-191) allowed the +incorrect size constant to persist. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AllocateSessionGlobalsArea allocates memory for a per-session GDI +globals block and then fills the buffer with several predefined tables +and default values. In the vulnerable build the buffer is obtained via + + PALLOCMEM(5752, 0x315E4C47) + +which reserves exactly 0x1678 (5752) bytes. Subsequent initialisation +stores reach offsets 0x1670, 0x1674 and later code paths reach 0x1678 +and 0x167C: + + * original code wrote only a 4-byte DWORD at offset 0x1670 + * other kernel routines later upgraded that location to a QWORD + (8 bytes) and introduced a new DWORD at 0x1678 + +Thus up to 12 bytes were written beyond the end of the 0x1678-byte +allocation, corrupting the adjacent pool chunk. The overwrite occurs +while the session is being brought up, before the session pointer is +published, so the attacker controls execution timing by creating/ +terminating sessions or by racing GDI API calls during logon. + +Corrupted data include pointers (e.g. the reference-tracker vtable at ++0x1670) and counters; overwriting them lets an attacker smuggle +arbitrary kernel pointers into follow-up dereferences and obtain kernel +code execution in the current session context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// allocation size (before) +v2 = (_OWORD *)PALLOCMEM(5752i64, 826754887i64); +… +// final writes that overflowed +*(_DWORD *)(v7 + 5744) = 0; // in-bounds +// later (other code) treated same field as QWORD and +// additionally touched +5752 -> overflow +``` +Patch +```c +// fixed allocation +v2 = (_OWORD *)PALLOCMEM(5760i64, 826754887i64); // +8 bytes +… +*(_QWORD *)(v7 + 5744) = 0i64; // now 8-byte store +*(_DWORD *)(v7 + 5752) = 0; // new field, still in range +``` +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Win32 session starts (e.g. logon or Fast User Switch). +2. W32GetSessionState() -> Gre::Base::AllocateSessionGlobalsArea(). +3. Function allocates 5752-byte pool block, fills defaults. +4. Copy of 8-byte data at +0x1670 or later write at +0x1678 overruns + the heap buffer, corrupting pool headers or neighbouring object. +5. Corruption is used by attacker-controlled data to escalate + privileges when the corrupted object is subsequently executed or + freed. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By creating a new desktop session or +forcing a session recycle (e.g. via RDP logon/off or Fast User Switch) +and racing GDI API calls, the attacker can influence the timing and +content of the out-of-bounds stores, leading to controlled kernel heap +corruption and privilege escalation. + +Patch Description +-------------------------------------------------------------------- +The fix increases the allocation size from 0x1678 (5752) to 0x1680 +(5760) bytes, aligning the buffer with the enlarged structure layout. +Additional constants have been moved to new, versioned tables and the +code now stores an 8-byte zero at +0x1670 and a 4-byte zero at +0x1678 +explicitly, both safely inside the new allocation. No other logic +changes were required. + +Security Impact +-------------------------------------------------------------------- +A kernel-mode heap buffer overflow allows arbitrary pool corruption. +Because the overwritten area contains pointers used by win32k, an +attacker can gain execution as NT AUTHORITY\SYSTEM, resulting in a +local elevation-of-privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated constant (5760) fully covers all current stores up to +offset 0x167C. Provided no future fields are appended without +updating the size, the immediate overflow is removed. No residual +race or integer issues remain visible in this site, but continued code +reviews are recommended whenever SESSION_GDI_GLOBALS grows again. diff --git a/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49760_storsvc.dll.txt b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49760_storsvc.dll.txt new file mode 100644 index 0000000..c0929db --- /dev/null +++ b/reports/2025-jul-12390-windows_11_24H2_x64/CVE-2025-49760_storsvc.dll.txt @@ -0,0 +1,134 @@ +{'kb': 'KB5062553', 'change_count': 14, 'date': 1752037661.84171, 'confidence': 0.24, 'patch_store_uid': '8b054c6c-22c7-4df0-9ed4-9b3236e74784', 'file': 'storsvc.dll', 'cve': 'CVE-2025-49760'} +-------------------------------------------------------------------- +CVE-2025-49760 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Storage Service (storsvc.dll) – directory-handling +helper classes found in RecursiveUtil::* and public entry point +StorageService::DeleteDirectoryTree/RecursiveScanDirectory. + +Vulnerability Class +-------------------------------------------------------------------- +Path spoofing / directory traversal caused by inconsistent and +incomplete canonicalisation of user-controlled paths +(CWE-73 External Control of File Name or Path). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. When StorageService is asked to delete or enumerate a directory it + eventually calls + RecursiveUtil::NtObjectPath::NtObjectPath(HANDLE) + to obtain a textual representation of the directory HANDLE that is + later compared – purely as a string – with other paths produced + during the recursion. + +2. Prior to the patch the constructor contained two code paths that + were selected by the runtime feature flag + Feature_Servicing_InvalidPathHandling (id 2578215227): + • enabled → safe path: GetFinalPathNameByHandleW(handle, + buf, 256, VOLUME_NAME_NT /*2*/, FILE_NAME_OPENED /*8*/) + returned a stable native NT path ("\\?\Volume{GUID}\..."). + • disabled → legacy path: GetFinalPathNameByHandleW was invoked + with no flags (default 0) and the resulting buffer was copied + into a std::wstring after a manual length scan. Depending on + the handle type this frequently produced an *alternate* + representation (e.g. "C:\dir" or "\\?\C:\dir") that is *not* + string-equal to the NT form although it refers to the same + object. + +3. RecursiveUtil::RecursiveScanDirectory() relies on + NtObjectPath::operator== to decide whether it is still operating + inside the original root. Because the comparison is textual, + providing a path that resolves to the same directory but that is + rendered in the alternative syntax bypasses the check. The service + will therefore recurse, copy or delete data outside the caller’s + authorised tree – a classic spoofing scenario. + +4. On top of the divergent path formats, the legacy code built + sub-paths with PathAllocCanonicalize / PathAllocCombine using option + 0x01 (PATHCCH_ALLOW_LONG_PATHS) which *does not* normalise all + segments. Dot-dot sequences or embedded NT device names could thus + survive and be concatenated with the un-normalised root producing + further traversal opportunities. + +5. The public API StorageService::DeleteDirectoryTree exposes the + vulnerable flow. An authenticated attacker that can feed a crafted + path (e.g. via an SMB share or local RPC call) can convince the + Storage Service that the target is inside an authorised directory + while it actually points elsewhere, leading to unintended actions + (creation, enumeration or deletion) on arbitrary file system + locations. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// NtObjectPath::NtObjectPath – OLD (unsafe branch) +wil::GetFinalPathNameByHandleW<unique_any_t<...>,256>(a2,&v9); // no flags +... +std::wstring::assign(this, v9); // blind copy +``` +```c +// NtObjectPath::NtObjectPath – NEW (always used) +int rc = wil::GetFinalPathNameByHandleW<std::wstring,256>( + a2, this, 2 /*VOLUME_NAME_NT*/, 8 /*FILE_NAME_OPENED*/); +if (rc < 0) throw; +``` +```c +// RecursiveScanDirectory – canonicalisation change +// OLD (flag depended) +PathAllocCanonicalize(path, 1 /*ALLOW_LONG_PATHS*/, &ppszPathOut); +// NEW (always) +PathAllocCanonicalize(path, 0x10 /*ENSURE_EXTENDED_LENGTH*/, &ppszPathOut); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +StorageService::DeleteDirectoryTree() + → RecursiveUtil::DeleteDirectoryTree() + – opens root handle + – NtObjectPath ← *unsafe formatting* + → RecursiveUtil::RecursiveScanDirectory() + – builds/combines child paths (legacy normalisation) + – compares NtObjectPath strings → spoofing bypass + → unintended file-system operations executed under SYSTEM account. + +Attack Vector +-------------------------------------------------------------------- +An authenticated user (local or over the network via SMB/RPC) supplies a +crafted directory name that: + • can be opened by the service, *and* + • has an alternative textual form (e.g. Volume GUID path vs drive + letter, extended-length "\\?\" prefix, mixed case, etc.). +String comparison fails, enabling the attacker to masquerade an +arbitrary directory as a sub-directory of an authorised tree, resulting +in spoofing of Storage Service operations. + +Patch Description +-------------------------------------------------------------------- +• Removed all feature-flag branches – the secure behaviour is now + unconditional. +• NtObjectPath always requests VOLUME_NAME_NT | FILE_NAME_OPENED, giving + a unique canonical NT path. +• All path builds now use PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH (0x10) + and no longer permit un-normalised segments. +• Large re-factoring of RecursiveScanDirectory / DeleteDirectoryTree to + pass validated parameters only. + +Security Impact +-------------------------------------------------------------------- +The service can no longer be tricked into treating two textual paths +that refer to the same directory as different, closing the spoofing +hole. Attempts to traverse outside the authorised root are stopped +because paths are normalised and compared in a single canonical form. +Potential consequences such as arbitrary directory deletion or data +corruption under SYSTEM privileges are eliminated. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable code paths have been completely removed; all callers are +forced through the safe canonicalisation routine with strict flag +settings. No residual alternate-format path can bypass the equality +check, so the fix fully addresses the identified weakness. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24065_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24065_storsvc.dll.txt new file mode 100644 index 0000000..5596b49 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24065_storsvc.dll.txt @@ -0,0 +1,111 @@ +{'kb': 'KB5060842', 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'confidence': 0.13, 'file': 'storsvc.dll', 'cve': 'CVE-2025-24065', 'date': 1763417624.8217874, 'change_count': 17} +-------------------------------------------------------------------- +CVE-2025-24065 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Service (storsvc.dll) – routines that validate user +controlled SP_PROVISIONING_INFO data supplied through the Storage +Management Provider. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-Bounds Read / Insufficient Input Validation (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The kernel-mode Storage Service exposes management IOCTLs that accept +an _SP_PROVISIONING_INFO structure originating in user space. Routine +ScValidateProvisioning() is the sole gatekeeper that is invoked before +the caller-supplied buffer is further processed. + +Before the patch the function verified only three fields: + • ProvisioningAction (DWORD 0) was limited to 0 or 1 + • If ProvisioningAction==1, flag a4 had to be set + • RangeCount (DWORD 4) had to be <=5 + • RangeStart (DWORD 5) 1–4 and RangeStart<=RangeEnd + • RangeEnd (DWORD 6) 1–4 and RangeStart<=RangeEnd + +No check whatsoever was performed for the next DWORD in the structure +(offset +28, i.e. *((DWORD*)a1+7)). Later code uses this value as a +boundary while iterating internal range arrays. A crafted buffer with +a7 set beyond the actual array size therefore causes the kernel to +read outside the allocated memory region, returning uninitialised +kernel data to user land – an information disclosure condition. + +The defect is purely a logic error: the author assumed a6 (RangeEnd) +was the last boundary value and forgot to clamp a7. Because the +structure is received directly from user space via DeviceIoControl, no +additional mitigation exists. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v7 = *((DWORD*)a1 + 5); // RangeStart +if ( v7 - 1 > 4 ) return ERR; +v8 = *((DWORD*)a1 + 6); // RangeEnd +if ( v8 - 1 > 4 || v7 > v8 ) // no test for DWORD 7 ! + return ERR; +... +// after +if ( (DWORD)(*((DWORD*)a1+5)-1) > 4 ) return ERR; +if ( Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() ) { + v8 = *((DWORD*)a1+6); + if ( v8 && (v8-1>4 || *((DWORD*)a1+5)>v8 || + v8 >= *((DWORD*)a1+7)) ) // new bound check + return ERR; +} +v9 = *((DWORD*)a1+7); // NEW – always checked +if ( v9-1 > 4 || *((DWORD*)a1+5) > v9 ) + return ERR; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User application issues a Storage Management Provider IOCTL with a + crafted _SP_PROVISIONING_INFO buffer. +2. The kernel dispatches to ScValidateProvisioning(). +3. Because DWORD 7 is unchecked, the malicious value passes validation. +4. Subsequent code in the Storage Service trusts the value and indexes + internal arrays past their end. +5. Kernel memory following the array is read and copied back to the + caller, disclosing sensitive information. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker (or sandbox escape) sending a malformed +SP_PROVISIONING_INFO buffer to the Storage Management Provider through +DeviceIoControl. No admin rights are required – only the ability to +open the device interface that is ordinarily accessible to normal +users. + +Patch Description +-------------------------------------------------------------------- +The update inserts two complementary validations: +1. When the H2E_WPA3SAE feature flag is enabled, DWORD 6 (RangeEnd) + must be 1–4, non-zero, RangeStart<=RangeEnd, and strictly less + than DWORD 7. +2. DWORD 7 is now always range-checked (1–4) and enforced to be >= + RangeStart. +The function returns error 0xC03A0011 when any of these tests fail. +All error paths are executed before the structure is used. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, arbitrary kernel memory could be disclosed to the +caller, potentially leaking kernel addresses, secrets or credential +material. This breaks KASLR and can be chained with other +vulnerabilities for privilege escalation. Patch eliminates the leak +path and converts it into a safe failure. + +Fix Effectiveness +-------------------------------------------------------------------- +The added checks cover the previously uncontrolled field and guarantee +that no index above 4 or below 1 is accepted, and that all ordering +constraints hold. As the validation occurs before any further use of +the buffer, the out-of-bounds read path is removed. No remaining code +paths referencing the field without validation were observed in the +diffs, indicating the fix is complete, although runtime paths gated by +undisclosed feature flags remain "unknown". diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24068_storport.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24068_storport.sys.txt new file mode 100644 index 0000000..e6c1853 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24068_storport.sys.txt @@ -0,0 +1,129 @@ +{'file': 'storport.sys', 'change_count': 39, 'confidence': 0.12, 'cve': 'CVE-2025-24068', 'patch_store_uid': '9630d929-42b0-4925-a299-159065ed4c3d', 'date': 1763415905.2834125, 'kb': 'KB5060842'} +-------------------------------------------------------------------- +CVE-2025-24068 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Storport mini-port interface – diagnostic IOCTL +handlers RaUnitStorageDiagnosticIoctl() and +RaidAdapterDiagnosticIoctl() in storport.sys (Storage Port Driver). +These routines service IOCTL_STORAGE_DIAGNOSTIC / SCSI_MINIPORT +requests that allow a user process to query a storage controller or +unit for vendor specific diagnostic data. + +Vulnerability Class +-------------------------------------------------------------------- +Buffer Over-read / Information Disclosure (CWE-126) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The handlers build a temporary non-paged-pool buffer ("P") by + calling RaBuildDiagnosticBufferForMiniport(). The size of this + allocation is returned in the variable AllocSize (v43 / v56). + +2. The contents of this buffer are interpreted as the following + structure (offsets in bytes): + +0x00 DWORD Signature + +0x0C DWORD Opcode + +0x3C DWORD DataLength ( *((DWORD *)P + 15) ) + +0x44 BYTE[ ] Variable data supplied by the mini-port driver + +3. Before the patch the copy that returns the mini-port data to the + caller was executed as + vLen = *((DWORD *)P + 15); // DataLength + if (vLen >= UserOutLen - 0x20) // cap **only** to the + vLen = UserOutLen - 0x20; // user buffer size + memmove(UserBuf+0x20+0x08, P+0x44, vLen); + + There is **no validation that 0x44 + vLen is still inside the pool + allocation P**. A malicious or buggy mini-port can therefore set + DataLength to a value larger than AllocSize-0x44 yet still smaller + than the user buffer. The subsequent memmove() reads past the end + of P and copies arbitrary kernel memory into the user supplied + output buffer. + +4. The same un-validated reads are performed while building ETW trace + events: the code walks a list of variable-length descriptors + located inside P (loop that uses v49 / v34 etc.) and dereferences + fields such as v25[12] and v25[13] before it has proved that + the descriptor lies completely inside P, producing additional + out-of-bounds reads. + +5. Because the copied data is returned to the calling process through + the IRP output buffer, arbitrary portions of kernel non-paged + memory become visible to user mode, defeating KASLR and helping in + further exploitation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – RaUnitStorageDiagnosticIoctl +vLen = *((DWORD *)v6 + 15); // length is fully controlled +if (vLen >= (int)UserOut - 32) // capped only to USER buffer + vLen = UserOut - 32; // NOT to internal pool buffer +memmove(UserBuf + 8, v6 + 0x44, vLen); // kernel over-read +``` + +```c +// after patch (simplified) +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + if (!RaidCallerIsAdmin() || CurrentThread!=Irp->Tail.Overlay...) { + return STATUS_PRIVILEGE_NOT_HELD; + } +} +// additional descriptor-length checks were added before every read +// and lengths are re-validated against the allocation size. +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens a Storport controlled device (eg. \\. + PhysicalDriveX). +2. DeviceIoControl(…, IOCTL_STORAGE_DIAGNOSTIC, …) is issued with a + crafted input buffer. +3. storport.sys receives IRP_MJ_DEVICE_CONTROL and calls + RaUnitStorageDiagnosticIoctl() or RaidAdapterDiagnosticIoctl(). +4. Function allocates buffer P, mini-port fills it. +5. Malicious DataLength in P causes memmove() to over-read kernel + memory and copy it into the caller’s output buffer. + +Attack Vector +-------------------------------------------------------------------- +Any local user that can send IOCTL_STORAGE_DIAGNOSTIC (or the SCSI +mini-port equivalent) to a Storport device can exploit the flaw. On +many systems this requires only a handle to a physical drive or RAID +adapter, which is often obtainable by non-administrators. + +Patch Description +-------------------------------------------------------------------- +1. Introduced an optional policy gate: if the new + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() flag is active + the caller must be running in an administrator context and on the + issuing thread; otherwise the request is rejected with + STATUS_PRIVILEGE_NOT_HELD. + +2. Re-worked descriptor parsing loops: every offset that is taken from + the untrusted mini-port buffer is now compared with the total buffer + size before it is dereferenced (added checks against v42 / Size[0] + etc.). If the check fails the code abandons the parse path. + +3. The same size checks are applied before copying data back to user + mode, ensuring that the copy length can never exceed the + mini-port-supplied buffer size. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could obtain arbitrary non-paged kernel +memory contents, including pointers and pool metadata. These leaked +values defeat KASLR and can be used to mount subsequent elevation-of- +privilege attacks. No code execution is achieved directly, but the +information disclosure materially lowers the bar for other exploits. + +Fix Effectiveness +-------------------------------------------------------------------- +The added privilege check removes the attack surface for non-admin +users, and the new bounds-checking logic guarantees that every read +and every memmove() stays within the allocated diagnostic buffer. +Therefore the over-read condition can no longer be triggered and the +information disclosure is remediated. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24069_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24069_storsvc.dll.txt new file mode 100644 index 0000000..e36bd1a --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-24069_storsvc.dll.txt @@ -0,0 +1,104 @@ +{'change_count': 17, 'cve': 'CVE-2025-24069', 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'date': 1763417266.138925, 'confidence': 0.35, 'file': 'storsvc.dll', 'kb': 'KB5060842'} +-------------------------------------------------------------------- +CVE-2025-24069 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Service (storsvc.dll) – routine responsible for +persisting per-volume state when a new storage card is discovered. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read / Memory-disclosure (CWE-125) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch, function +StorageService::DeterminePersistentVolumeState() accepted four user +supplied parameters: + a1 – StorageService instance (this) + a2 – WCHAR* path to the volume + a3 – _STORAGE_DEVICE_TYPE (indexed as integer) + a4 – volume ordinal (unsigned int) + +The routine called itself recursively and, on success, wrote the +returned DWORD into an internal per-volume cache using the following +calculation: + + *(_DWORD*)( *((QWORD*)a1 + a3 + 5) + 1112*a4 + 1104 ) = v10[0]; + +The code assumed that ‘a3’ and ‘a4’ were inside the ranges of the +StorageService object’s internal arrays, but **no validation was +performed**. Supplying values that exceed the real number of device +types or volumes caused the inner pointer arithmetic to refer past the +end of the containing allocation. + +Because the pointer is then forwarded to the caller by subsequent +functions, a crafted client can coerce storsvc (running as SYSTEM) to +read and later disclose arbitrary kernel heap contents belonging to the +process. The issue is purely an out-of-bounds *read*; the 32-bit value +is copied into user-observable structures but never written back to the +out-of-range address, keeping integrity intact yet leaking confidential +information. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (simplified) +unsigned int v10[6]; +... +StorageService::DeterminePersistentVolumeState(a1, a2, v10); +if (v7 >= 0) { + *(_DWORD *)(*((_QWORD *)a1 + a3 + 5) + 1112 * a4 + 1104) = v10[0]; + return 0; +} +``` + +```c +// after patch – entire body replaced +if ((Feature_Servicing_RackLevelNestedMirror__private_featureState & 0x10) != 0) + return Feature_Servicing_RackLevelNestedMirror__private_featureState & 1; +else + return Feature_Servicing_RackLevelNestedMirror__private_IsEnabledFallback(...); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client invokes StorageService::ProcessNewStorageCard() +2. ProcessNewStorageCard() calls DeterminePersistentVolumeState() with + user-controlled ‘a3’ (device type) and ‘a4’ (volume index). +3. DeterminePersistentVolumeState() dereferences a1 + a3 + 5 and then + applies 1112*a4 offset – no bounds check. +4. Out-of-range read value is later returned to user, disclosing kernel + memory. + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker can issue IOCTLs / WMI calls that +request Storage Service to enumerate or mount a forged storage card. +By providing crafted device-type and disk-number parameters the service +is tricked into executing the out-of-bounds access. + +Patch Description +-------------------------------------------------------------------- +Microsoft removed the vulnerable logic entirely. The old helper is no +longer used; instead, a new, small wrapper (Feature_H2E_WPA3SAE__ +private_IsEnabledDeviceUsage) returns a feature-flag value and delegates +to a safe fallback routine. All pointer arithmetic that accessed the +per-volume array has been deleted, so no untrusted indices reach the +internal cache. + +Security Impact +-------------------------------------------------------------------- +Successful exploitation discloses up to 4 bytes of arbitrary kernel +heap memory per request, enabling information leaks that may aid in +further privilege-escalation or ASLR bypass attacks. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable function body is removed; no memory index calculation is +left. Because the replacement routine performs only flag checks and +uses constant addresses, there is no longer a path that accepts attacker +controlled indices. Therefore the out-of-bounds read is fully +mitigated. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-29828_schannel.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-29828_schannel.dll.txt new file mode 100644 index 0000000..5a96321 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-29828_schannel.dll.txt @@ -0,0 +1,116 @@ +{'change_count': 3, 'patch_store_uid': '6cd4119d-6eb0-427c-ac5a-466792602588', 'cve': 'CVE-2025-29828', 'kb': 'KB5060842', 'date': 1763415487.922896, 'confidence': 0.22, 'file': 'schannel.dll'} +-------------------------------------------------------------------- +CVE-2025-29828 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Schannel – schannel.dll +Function: CTls13ServerContext::DowngradeServerToSsl3Tls + +Vulnerability Class +-------------------------------------------------------------------- +CWE-401: Missing release of memory after effective lifetime +(Resource-management error that can be leveraged for remote code +execution) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine DowngradeServerToSsl3Tls is reachable during a TLS 1.3 +handshake whenever the server elects to fall back to an earlier +SSL3/TLS protocol version. In the original implementation the +function performed the following sequence: + +1. Always invoked CSslContextManager::AllocateServerContext() to + obtain a fresh CSslParentContext object (returned in local "v3"). +2. If the call succeeded, it unconditionally overwrote the pointer + located at *(this + 231)->+24 with the newly returned context + pointer (v3+2). +3. No attempt was made to free or reference-count the previous object + already stored in that field. As a consequence every additional + downgrade attempt leaked one CSslParentContext instance. +4. The field *(this + 232) was ORed with 0x40000, signalling that the + downgrade had been completed, but this flag did not prevent the + function from being entered again later in the handshake. + +Because the downgrade helper can be triggered more than once during a +single network session (e.g. by manipulating the handshake message +sequence or by starting several renegotiations), an unauthenticated +remote attacker can force an unbounded number of allocations. The +leaked objects remain permanently unreachable, causing uncontrolled +heap growth inside the LSASS process that hosts Schannel. When the +allocator finally reuses the exhausted region, pointer corruption may +occur, enabling remote code execution in the context of the process. + +Key object layout (offsets are 8-byte indices into *this): + +231 -> CTls13SecurityParameters + +24 PVOID pServerContext (overwritten, never released) + +232 -> DWORD64 ContextFlags (bit 0x40000 set on downgrade) + +273 -> PVOID pExistingDowngrade (non-zero when a context exists) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch +v3 = 0; +result = CSslContextManager::AllocateServerContext(&g_SslContextManager, + this, &v3); +if (!result) { + result = 1359; // ERROR_UNKNOWN + *((QWORD*)(*((QWORD*)this + 231)) + 3) = *((QWORD*)v3 + 2); + *((QWORD*)this + 232) |= 0x40000; // mark downgraded +} +``` +```c +// after patch (excerpt) +if ( FeatureEnabled() && *((QWORD*)this + 273) ) { + TraceDowngradeDenied(); + return 0x80090332; // SEC_E_UNSUPPORTED_FUNCTION +} +// old leak-prone logic kept under the else-branch +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Network packet -> Schannel handshake parser + -> CTls13ServerContext::ProcessClientHello + -> DowngradeServerToSsl3Tls (called repeatedly) + -> Multiple allocations without release + +Attack Vector +-------------------------------------------------------------------- +An unauthenticated attacker sends a crafted TLS 1.3 handshake followed +by renegotiation messages that repeatedly force the server to downgrade +its security context. Every renegotiation re-enters +DowngradeServerToSsl3Tls, leaking memory inside the Schannel service. +No local access is required; exploitation is entirely over the network +on any service that uses Schannel (e.g. IIS, RDP). + +Patch Description +-------------------------------------------------------------------- +The fix adds a guard clause: +1. A feature flag check using the Windows Feature gating mechanism. +2. If the feature is enabled *and* *(this + 273) is non-null (meaning a + downgrade context is already present), the function aborts early and + returns SEC_E_UNSUPPORTED_FUNCTION (0x80090332). +3. Optional WPP tracing is performed for diagnostics. + +This prevents repeated allocations and therefore eliminates the memory +leak. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could carry out a persistent heap leak +that eventually leads to memory exhaustion or to re-allocation of stale +objects in freed regions, enabling arbitrary code execution under the +LocalSystem account that hosts LSASS. Successful exploitation grants +full control of the target machine over the network. + +Fix Effectiveness +-------------------------------------------------------------------- +The added predicate ensures that the potentially leaking code path can +run at most once per connection, completely blocking the unbounded leak +scenario. No additional memory handling changes were required, so the +patch adequately addresses the root cause. No bypass is evident from +the diff. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-3052_securekernel.exe.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-3052_securekernel.exe.txt new file mode 100644 index 0000000..94f345f --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-3052_securekernel.exe.txt @@ -0,0 +1,115 @@ +{'date': 1763415539.2983875, 'kb': 'KB5060842', 'cve': 'CVE-2025-3052', 'change_count': 14, 'file': 'securekernel.exe', 'patch_store_uid': 'bc997d55-eb24-42a5-8f63-beadfbb4d7ef', 'confidence': 0.15} +-------------------------------------------------------------------- +CVE-2025-3052 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +securekernel.exe – early-boot processor-identification helpers +(RtlGetProcessorSignature, RtlGetCpuVendor, friends) + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted Pointer Dereference / Arbitrary Kernel Write +(CWE-822) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper routine RtlGetProcessorSignature originally accepted up to +five caller-supplied pointer arguments that were always blindly +written: + *a1 = vendor ; + *a2 = family ; + *a3 = model ; + *a4 = stepping; + *a5 = extID ; + +No validation was performed – the addresses could be NULL or point +anywhere in kernel or even user space. Because the function executes +very early in Secure Kernel initialisation (and is also reused by +later runtime paths such as SkiSetFeatureBits, RtlpInitFunctionOverride +Capabilities, etc.) a malicious or simply buggy caller could supply an +attacker-controlled pointer and force the secure kernel to store CPU +information to that location. + +Consequences: +• NULL pointers produced an immediate SK bugcheck during boot. +• Non-canonical or attacker-chosen addresses enabled an arbitrary + 1-byte/2-byte/4-byte write from EL0-influenced context inside the + secure kernel, defeating the Secure-Boot trust boundary. + +Structurally, the fault sits in securekernel!RtlGetProcessorSignature +at address 0x1400C55A4 (pre-patch). Similar helper RtlGetCpuVendor +returned a 64-bit value although only one byte was ever used; this +mismatch propagated uncontrolled data further. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// PRE-PATCH (truncated) +char __fastcall RtlGetProcessorSignature(_BYTE *a1,_BYTE *a2,_WORD *a3, + _BYTE *a4,_BYTE *a5) +{ + ... + *a1 = RtlGetCpuVendor(); // <== unconditional writes + *a2 = BYTE1(v13) & 0xF; + *a3 = (unsigned __int8)(v13 >> 20); + *a4 = (v13 >> 4) & 0xF; + *a5 = BYTE2(v13) & 0xF; +} + +// POST-PATCH +__int64 __fastcall RtlGetProcessorSignature(_BYTE *a1,int *a2,int *a3, + _DWORD *a4) +{ + ... + if (a1) *a1 = CpuVendor; + if (a2) *a2 = v14; // family + if (a3) *a3 = v15; // model + if (a4) *a4 = result & 0xF; // stepping + return (unsigned)result; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker reaches secure-kernel API that eventually calls + RtlGetProcessorSignature. +2. Supplies crafted pointer arguments (NULL or arbitrary address). +3. Function dereferences the pointer without checks, writing CPU id + bytes to that address. +4. Depending on pointer, results in bugcheck or controlled write that + can be leveraged to bypass Secure Boot policy. + +Attack Vector +-------------------------------------------------------------------- +Local, authorised code running during boot or within the secure kernel +context can supply untrusted pointers to the helper routine and obtain +an arbitrary kernel write. + +Patch Description +-------------------------------------------------------------------- +1. RtlGetProcessorSignature signature changed from five generic output + pointers to four typed pointers. +2. Added explicit nullptr checks before every dereference. +3. RtlGetCpuVendor now returns a single byte (char) instead of 64-bit + integer, removing unused upper bytes. +4. All callers (RtlDetectProcessorFeatures, SkiSetFeatureBits, + SkiSetProcessorSignatureAndFlags, etc.) updated to pass either + stack variables or NULL as appropriate. + +Security Impact +-------------------------------------------------------------------- +Before the patch a user-controlled pointer could be written by secure +kernel code, resulting in potential Secure Boot bypass, privilege +escalation to the secure kernel context, or a guaranteed denial of +service (boot crash). + +Fix Effectiveness +-------------------------------------------------------------------- +The added NULL checks prevent write-to-NULL and prevent writes when +callers intentionally pass no buffer. Because callers were also +audited to pass only stack-backed variables (or NULL) the primitive +arbitrary write is removed. No remaining unvalidated pointer writes +are observable in the updated code, so the fix is considered +effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32712_win32k.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32712_win32k.sys.txt new file mode 100644 index 0000000..c61f4c9 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32712_win32k.sys.txt @@ -0,0 +1,114 @@ +{'confidence': 0.26, 'patch_store_uid': 'ed1ea283-2bd8-4b7d-8cde-f996a3252e08', 'file': 'win32k.sys', 'date': 1763417324.8764412, 'cve': 'CVE-2025-32712', 'change_count': 3, 'kb': 'KB5060842'} +-------------------------------------------------------------------- +CVE-2025-32712 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys – ApiSet resolver code path (ApiSetResolveToHost, +ApiSetResolveToHost_V7 and ApiSetpSearchForSectionIndex_V7). + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / stale-pointer dereference caused by 64-bit-to-32-bit +pointer truncation (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ApiSetResolveToHost receives a pointer (a1) that references an ApiSet +mapping header located in kernel virtual memory. On x64 builds the +pointer is 64 bits. In the pre-patch routine the following logic is +executed when the first byte of the header indicates schema version 7: + + if (*a1 == 7) + return ApiSetResolveToHost_V7((_DWORD)a1, ...); + +The cast to _DWORD truncates the upper 32 bits of the pointer before it +is forwarded to ApiSetResolveToHost_V7. If the original allocation +resides above the 4 GB boundary (normal on modern systems) the truncated +value now points to an unrelated location in the lower address space – +often memory that has already been freed and possibly re-allocated for a +completely different purpose. ApiSetResolveToHost_V7 then treats that +stale address as a live ApiSetV7 structure, dereferences multiple inner +pointers and finally writes a host-string descriptor into a caller +supplied buffer (a5). All of those accesses operate on attacker +controlled memory, resulting in a classic use-after-free inside the +kernel. + +Attacker influence +• The attacker controls where the ApiSet blob is allocated; by forcing a + high address it guarantees pointer truncation. +• After the legitimate blob is freed, the attacker can reclaim the low + 4 GB address space with controlled data that masquerades as an ApiSet + structure, steering subsequent kernel reads/writes. + +Consequences +• Arbitrary kernel read/write through forged internal offsets. +• Elevation of privilege from an unprivileged local context to SYSTEM. + +Patch changes +1. Function prototype changed to use a 64-bit parameter + ( __int64 a1 ) and the call to ApiSetResolveToHost_V7 now passes the + unmodified 64-bit value, eliminating truncation. +2. The original base pointer is kept in a local variable (v6) and is + consistently used for all offset calculations, preventing + mis-calculations when a nested V7 header is encountered. +3. Additional sanity for version-6-with-embedded-V7 headers was added. +4. ApiSetpSearchForSectionIndex_V7 was rewritten to use relative offsets + that account for the 16-bit base stored in the ApiSet header and now + relies on RtlCompareUnicodeStrings instead of a home-grown loop. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// --- pre-patch --- +if (*a1 == 7) + return ApiSetResolveToHost_V7((_DWORD)a1, (_DWORD)a2, a3, + (_DWORD)a4, a5); // 32-bit truncation + +// --- fixed --- +if (*(_BYTE *)a1 == 7) + return ApiSetResolveToHost_V7(a1, a2, a3, a4, (_OWORD *)a5); // full ptr +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process causes win32k to load/construct an ApiSet mapping that + resides above 0xFFFFFFFF. +2. Kernel enters ApiSetResolveToHost via CreatePerSessionWin32kCall. +3. *a1 == 7 branch taken –> pointer truncated -> ApiSetResolveToHost_V7. +4. ApiSetResolveToHost_V7 interprets attacker-controlled fake header at + the truncated address and performs arbitrary memory operations. +5. Crafted data redirects execution / corrupts kernel state, yielding + SYSTEM privileges. + +Attack Vector +-------------------------------------------------------------------- +Local authenticated attacker. By manipulating per-session ApiSet +section allocation and heap reuse, the attacker positions a controlled +buffer at the truncated 32-bit address, then invokes a win32k API that +ultimately calls ApiSetResolveToHost. + +Patch Description +-------------------------------------------------------------------- +• Replaced all 32-bit casts with native 64-bit pointer handling. +• Added support for version-6 headers that embed a version-7 header + (guards and correct offset adjustments). +• Re-implemented index search and string comparison routines to work + with relative offsets and validated lengths. +• All internal pointer arithmetic now begins with the original base + pointer, removing the possibility of stale or mis-aligned accesses. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could trigger kernel use-after-free and +obtain arbitrary kernel read/write, resulting in elevation of privilege +(EoP) to SYSTEM. Scope of impact: full compromise of the OS kernel. + +Fix Effectiveness +-------------------------------------------------------------------- +The fundamental issue – pointer truncation – has been removed, and every +code path now carries the intact 64-bit address. Calculations that +formerly relied on a possibly shifted pointer now reference the stored +base, closing the use-after-free avenue. No residual truncations are +present in the modified code, so the fix is considered effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32713_clfs.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32713_clfs.sys.txt new file mode 100644 index 0000000..7c64a42 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32713_clfs.sys.txt @@ -0,0 +1,134 @@ +{'file': 'clfs.sys', 'cve': 'CVE-2025-32713', 'kb': 'KB5060842', 'date': 1751957929.8085032, 'change_count': 2, 'confidence': 0.22, 'patch_store_uid': '25426e49-c7fc-4032-9c22-9634c901b910'} +-------------------------------------------------------------------- +CVE-2025-32713 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Common Log File System Driver (clfs.sys) – routine +CClfsLogFcbPhysical::ReadLogBlock() + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow (CWE-122) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ReadLogBlock() copies data from a log container into a caller supplied +_CLFS_READ_BUFFER (parameter a5). The caller also supplies the maximum +buffer size in a6 and optional control flags in a4/a8. + +1. The function computes the number of bytes that will be copied in the + local variable v27 (renamed v28 in the patched file). If the call + is the first iteration of a physical read ( (a4 & 1) != 0 and + *a10 == 0 ) the original code unconditionally rounds this length up + to the CLFS log-page size stored in v48: + + v27 = v48; // page size (typically 0x1000) + + This assignment is made without verifying that v27 is + (a) a multiple of the underlying sector size, or + (b) smaller than or equal to the caller supplied buffer length a6. + +2. Later, two different paths move v27 bytes into the user buffer: + • CClfsLogFcbPhysical::ReadLog(..., v27 >> 9) – async path + • CcCopyRead() / memset() – cache path + + Because v27 may now be larger than a6, both paths write past the end + of the heap allocation returned by _CLFS_READ_BUFFER::GetAddress(), + corrupting pool memory in the kernel. The overrun can be triggered + from user mode because both the buffer size (a6) and the control + flags (a4 / a8) are caller-controlled parameters that travel through + the public CLFS API (e.g. ClfsReadLogRecord()). + +3. In addition, nothing ensured that v27 was sector-aligned. A value + that is not a multiple of the physical sector size is later divided + by 512 ( v27 >> 9 ) when building the request for ReadLog(), causing + a second length mismatch between what the disk read returns and what + is later copied/memset – another source of overflow. + +The combined lack of size and alignment validation therefore allows a +local attacker to overflow a kernel heap buffer and execute arbitrary +code in kernel context. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (first iteration, flag 0x1 set) +if ( (v73 & 1) != 0 ) // physical read flag +{ + if ( !*a10 ) // first chunk + { + v27 = v48; // force page size (0x1000) + v47 = v48; + v28 = 1; + } +} +// later – copy v27 bytes into user buffer v51 +CcCopyRead(a2, &FileOffset, v27, 1u, v51, &IoStatus); +``` + +```c +// post-patch – new validation +if ( Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_4() && + (v28 % v48) ) // not page aligned? +{ + v12 = STATUS_INVALID_PARAMETER; + goto fail; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode NtCreateFile -> CreateLogFile() … +CLFS API ClfsReadLogRecord( … small_buffer … ) +clfs.sys CClfsReadIoctl() -> ReadLogBlock() +ReadLogBlock() length rounded up to v48 (0x1000) + CcCopyRead/ReadLog overwrite a6-byte heap + buffer => overflow + + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker opens or creates a CLFS log file and +issues a CLFS read request with: + • buffer length (a6) < log page size, and + • CLFS_READ_FLAG_PHYSICAL (bit 0) set. +No special privileges are required beyond the ability to open the file. + + +Patch Description +-------------------------------------------------------------------- +The update inserts multiple defensive checks: +1. Reject any requested length that is not an integer multiple of the + log page size: + if (FeatureEnabled && len % PageSize) return STATUS_INVALID_PARAMETER; +2. During the cached read path, additionally verify that + FileOffset + len <= LogFileSize and + RemainingBuffer >= len + before performing CcCopyRead(). +3. Early-exit paths were re-structured so that failures unwind with the + resource lock always released. +No changes are made to memory allocations; the fix is purely validation +logic that prevents inconsistent length calculations. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could cause a controlled heap based +overflow inside clfs.sys, allowing escalation from user to kernel +privileges. Successful exploitation yields arbitrary code execution in +ring-0, compromising the entire operating system. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added alignment and bounds checks block all paths that previously +produced an over-length copy. Because the size is now validated before +any I/O, the vulnerable memcpy/CcCopyRead cannot be reached with an +oversized value. No bypass is apparent in the modified logic, making +the fix effective for the identified issue. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32714_msi.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32714_msi.dll.txt new file mode 100644 index 0000000..c2408a5 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32714_msi.dll.txt @@ -0,0 +1,129 @@ +{'kb': 'KB5060842', 'change_count': 13, 'confidence': 0.27, 'cve': 'CVE-2025-32714', 'date': 1763416843.9731092, 'patch_store_uid': 'a368fe0d-9e6c-4521-9671-fa9b783254fa', 'file': 'msi.dll'} +-------------------------------------------------------------------- +CVE-2025-32714 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Installer (msi.dll) – runtime file-operation helpers: + • CMsiOpExecute::ReplaceFileOnReboot + • CMsiOpExecute::ixfFileRemove + • CMsiOpExecute::CreateFileFromData + • CMsiExecute::RunInstallScript + • VerifyMsiObjectAgainstSAFER + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Privilege-elevation through unsafe system +file replacement (CWE-284). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch, CMsiOpExecute::ReplaceFileOnReboot() blindly +invoked + MoveFileExW(src,dst,MOVEFILE_DELAY_UNTIL_REBOOT|…) +under the SYSTEM account. The routine was reachable from a user- +provided MSI script (ixfFileRemove → ReplaceFileOnReboot). Nothing +verified that + • the caller owned the source file, or + • the destination path was outside the protected system drive, or + • the invoking token was administrative. + +Consequently a non-privileged user could embed a crafted +FileRemove/FileReplace action in a locally-run MSI. When the +installer service (msiexec.exe, running as NT AUTHORITY\SYSTEM) +processed the script it scheduled an arbitrary, attacker-controlled +file to overwrite a high-integrity system file after reboot, leading +to full SYSTEM code execution. + +Patch adds: + 1. A feature-gate (Feature_3843093816) and helper class + AppIdHelper used to collect operation context. + 2. CheckSystemDrivePath() – validates the *source* path lies on the + system drive and returns a classification. + 3. Enforcement logic: + if (!IsAdmin() && !GetAISToken() && !GetAISFlag() && + CheckSystemDrivePath()!=1) + abort; + 4. Early returns (goto LABEL_28) before the dangerous MoveFileExW + call when the above test fails. + +Similar privilege / path-validation hooks are inserted in +CreateFileFromData(), ixfFileRemove() and RunInstallScript() so that +file creation, deletion and script execution now collect file +information and respect the same policy checks. VerifyMsiObject- +AgainstSAFER() is instrumented to reset AppIdHelper state so that the +new policy is consistently applied. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – ReplaceFileOnReboot, no privilege / path checks +CElevate elev(1); +MoveFileExW(src,dst, MOVEFILE_DELAY_UNTIL_REBOOT); +``` + +```c +// after – privilege & path validation +if (FeatureEnabled()) { + AISToken = GetAISToken(); + AISFlag = GetAISFlag(); + if (CheckSystemDrivePath(src,&flag)!=1) goto fail; + if (!IsAdmin() && !AISToken && !AISFlag && flag!=1) + goto fail; // blocks un-privileged caller +} +CElevate elev(1); +MoveFileExW(src,dst, MOVEFILE_DELAY_UNTIL_REBOOT); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege attacker launches msiexec /i evil.msi +2. CMsiExecute::RunInstallScript parses embedded actions +3. ixfFileRemove → ReplaceFileOnReboot executed as SYSTEM +4. Pre-patch: MoveFileExW schedules overwrite of + %SystemRoot%\system32\drivers\vuln.sys with attacker file +5. Reboot → system file replaced → attacker gains SYSTEM + + +Attack Vector +-------------------------------------------------------------------- +Local. An authenticated user crafts / modifies an MSI package that +contains a FileRemove or ReplaceFile action targeting an arbitrary +system-drive file and installs it on the machine. + + +Patch Description +-------------------------------------------------------------------- +• Introduces new WIL feature gate “AutoIdentityGenerationHook”. +• Adds AppIdHelper to collect user SID, package name and per-file + metadata. +• Inserts privilege & path checks (IsAdmin, AIS token, system-drive + path) before any MoveFileExW / Delete / CreateFile that targets + reboot-time replacement. +• Blocks the operation and returns error 0/3 when validation fails. +• Additional instrumentation in helper functions ensures the same + policy is enforced for file creation and removal during install. + + +Security Impact +-------------------------------------------------------------------- +Before the fix, any local user could escalate to NT AUTHORITY\SYSTEM +by scheduling the replacement of a protected system file via Windows +Installer. The patch prevents non-elevated callers from touching +system-drive paths unless they possess administrative rights or a +special AIS token. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added privilege and path validation removes the direct +vulnerability surface. File-replace requests from non-administrators +now fail early, and normal administrative installs continue to work. +No bypass is known with the patched logic; residual risk is +considered low. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32715_mstsc.exe.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32715_mstsc.exe.txt new file mode 100644 index 0000000..1e7e55c --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32715_mstsc.exe.txt @@ -0,0 +1,123 @@ +{'kb': 'KB5060842', 'change_count': 9, 'file': 'mstsc.exe', 'cve': 'CVE-2025-32715', 'date': 1763415523.033708, 'patch_store_uid': '0b61dfb1-c5c4-4dc3-9b9a-958674f888c0', 'confidence': 0.34} +-------------------------------------------------------------------- +CVE-2025-32715 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Remote Desktop Protocol (RDP) client – mstsc.exe +Function: CRailContWndExt::AddRemoteApplicationToQueueUi() +This helper is used by the RemoteApp (RAIL) feature to insert a +server-supplied application entry – together with its icon – into the +local Connection Queue UI. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-Bounds Read / Un-trusted File Loading (CWE-125) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The RAIL protocol allows the server to provide an icon path (argument +`a2`) for every published application. Prior to the patch the client +code performed **no validation** of this path: + +1. `LoadImageW(NULL, a2, IMAGE_ICON, …, LR_LOADFROMFILE | … )` was + invoked unconditionally. +2. When the path pointed to a UNC or other network location the RDP + client fetched and parsed the file fully on the local system. +3. The ICO parser (inside USER32/GDI) is not hardened against malformed + data; a specially-crafted icon can trigger an out-of-bounds read + during parsing, causing disclosure of adjacent memory to the + attacker-controlled channel (the RDP virtual channel or a crash with + memory dump). + +No size / scheme / network checks were executed before accessing the +icon, therefore any RDP server, even one without file-share access, was +able to point the client at an SMB/WebDAV share containing a malicious +ICO and force the vulnerable read. + +Patch-added logic introduces two independent gates before the icon is +loaded: + +• Feature flag `Feature_MSRC88443` (WinExp feature mechanism). +• Registry key `HKCU\Software\Microsoft\RemoteApplications\ + AllowRemoteAppIconFromNetworkPath` (DWORD, default 0). + +Only if *either* gate explicitly allows remote icons **and** the path is +not a network location (`!PathIsNetworkPathW(a2)`) will `LoadImageW` be +executed. Otherwise a local fallback icon is used. As a result network +content is no longer parsed in the normal configuration and the OOB read +condition is removed. + +Additional hardening added by the patch: +• Symmetric cleanup for every early-return path (`DestroyIcon`, + `LocalFree`). +• Double `memset` to clear the `lParam` stack buffer before use. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – unconditionally load server-supplied file +ImageW = (HICON)LoadImageW(NULL, a2, IMAGE_ICON, 0, 0, 0x2050); + +// AFTER – reject network paths unless explicitly allowed +if ((!wil::Feature<Feature_MSRC88443>().IsEnabled() || + CUT::UT_ReadRegistryInt(L"RemoteApplications", + L"AllowRemoteAppIconFromNetworkPath", 0, 1)) && + a2 && (RegistryInt || !PathIsNetworkPathW(a2)) && + (IconW = (HICON)LoadImageW(NULL, a2, IMAGE_ICON, 0, 0, 0x2050))) +{ + v31 = 1; // custom icon accepted +} +else +{ + IconW = LoadIconW(hInst, MAKEINTRESOURCEW(0x65)); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Malicious RDP server advertises a RemoteApp with `RAIL_ICON_PATH` set + to `\\attacker\share\bad.ico`. +2. mstsc.exe receives the message and calls + CRailContWndExt::AddRemoteApplicationToQueueUi(). +3. On an unpatched client the function calls `LoadImageW` on the remote + file. +4. The crafted ICO triggers an out-of-bounds read inside USER32 as the + parser processes invalid directory/data offsets. +5. Memory content is returned to the attacker (e.g., via RDP channel or + crash dump), constituting information disclosure. + +Attack Vector +-------------------------------------------------------------------- +Network-borne. Any RDP server (including malicious or compromised ones) +that can reach the client can supply a UNC path to a crafted icon file +and cause the vulnerable code path to be executed automatically when the +user connects. + +Patch Description +-------------------------------------------------------------------- +• Introduced a feature flag plus registry override controlling whether + remote icon paths are permitted. +• Added `PathIsNetworkPathW` check – by default network locations are + rejected and a built-in resource icon is used. +• Ensured proper cleanup on all exit paths and avoided destroying icons + when the new feature flag keeps them alive. +• Updated tracing GUIDs (cosmetic). + +Security Impact +-------------------------------------------------------------------- +Before the fix, a malicious RDP server could coerce the client into +loading and parsing attacker-supplied ICO data, enabling an +out-of-bounds read that may leak process memory and potentially assist +in further exploitation. The issue is classified as Information +Disclosure with network attacker context (CVE-2025-32715). + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code prevents remote paths from reaching `LoadImageW` unless +an administrator explicitly opts-in via registry or feature flag. This +removes the ability for an attacker to supply arbitrary icon data and +therefore eliminates the immediate OOB read condition. Added cleanup +paths also ensure no new resource leaks or double frees were +introduced. The fix is considered effective for the described issue. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32718_mrxsmb.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32718_mrxsmb.sys.txt new file mode 100644 index 0000000..627e749 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32718_mrxsmb.sys.txt @@ -0,0 +1,137 @@ +{'cve': 'CVE-2025-32718', 'kb': 'KB5060842', 'patch_store_uid': '238e095d-a913-4a7d-b7e3-94a2acac5569', 'change_count': 5, 'confidence': 0.3, 'file': 'mrxsmb.sys', 'date': 1763415486.9759088} +-------------------------------------------------------------------- +CVE-2025-32718 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +MRxSmb.sys – Windows kernel-mode SMB (redirector) client driver. +Affected helper routines: + • SmbCeCreateSrvCall + • RxCeEncryptData + • RxCeCompressData + + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow / heap-based buffer overflow (CWE-190, CWE-122). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Two data-path helper routines allocate a pool buffer whose size is +calculated as + requested_data_length + constant_offset +and then copy ‘requested_data_length’ bytes into this buffer. + +1. RxCeEncryptData + • Original code stores `total = a4 + 132` and allocates that many + bytes with ExAllocatePool2. + • No verification is made that the 32-bit user-controlled value + `a4` can be safely added to 132. + • If a4 is close to 0xFFFFFFFF, the 32-bit addition wraps, leading + to a much smaller allocation while the subsequent call to + RtlCopyMdlToBuffer writes the full (large) amount starting at + offset 132 (Pool2+132). Heap memory behind the allocation is + overwritten. + • The same buffer is later handed to IoAllocateMdl, extending the + corruption to MDL handling structures. + +2. RxCeCompressData (both compression and legacy paths) + • `alloc_size = a4 + (a5?96:16)` is computed with 32-bit arithmetic + and used for ExAllocatePool2. No overflow check exists. + • The function then copies/encodes up to `a4` bytes plus protocol + headers into the buffer, again overrunning the heap allocation if + the addition wrapped. + +3. SmbCeCreateSrvCall + • The routine itself is not the overflow source, but it later + references the corrupted buffers and can propagate the corrupted + pointers, making exploitation paths easier. + +The attacker controls `a4` because it is derived from remote SMB +payload length fields that the client parses when it encrypts or +compresses outgoing data or when loop-back operations are performed. +Because the routines execute in kernel context, any heap corruption +occurs in the kernel NonPagedPool, enabling elevation of privilege. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// RxCeEncryptData – vulnerable +*a6 = a4 + 52; // length for MDL +Pool2 = ExAllocatePool2(66, a4 + 132, 'SmBf'); +... +RtlCopyMdlToBuffer(a3, 0, Pool2 + 132, a4, &BytesCopied); // overflow +``` + +```c +// RxCeCompressData – vulnerable +unsigned int alloc = a4 + (a5 ? 96 : 16); +Pool2 = ExAllocatePool2(66, alloc, 'SmBf'); +... +SmbCompressionLegacyCompress(... , alloc_buffer + 16, &outLen); +``` + +```c +// Patch – added overflow check +if (Feature_IsEnabled() && (a4 + 52) < 0x34) { + *a6 = -1; // fail – integer wrapped / too small + return STATUS_INTEGER_OVERFLOW; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. A user-mode process or malicious SMB server supplies a crafted SMB + message that requires encryption/compression with a huge `Length` + field (`a4`). +2. MRxSmb.sys calls RxCeEncryptData / RxCeCompressData with that length. +3. Addition overflows; ExAllocatePool2 returns a small buffer. +4. RtlCopyMdlToBuffer / compression helpers write past the end of the + buffer into adjacent NonPagedPool memory. +5. Corrupted heap metadata or adjacent objects allow arbitrary kernel + memory overwrite → elevation of privilege. + + +Attack Vector +-------------------------------------------------------------------- +Local: an attacker running in user space can trigger the SMB client to +send a crafted buffer (e.g., via SMB loopback share) or exploit a +privileged service that uses the SMB client. Remote: a malicious SMB +server can return a response that causes the client to execute the +vulnerable paths during encryption/compression setup. + + +Patch Description +-------------------------------------------------------------------- +1. Added explicit overflow detection before size arithmetic: the code + now checks that `requested + header` does not wrap and meets minimum + header sizes. On failure it returns STATUS_INVALID_BUFFER_SIZE + (0xC000009B). +2. Re-ordered structure layout: new constant 80-byte header, pointer + arithmetic updated (`buffer+80`, `buffer+132`) to ensure alignment. +3. Early exit paths added so allocation and copy are skipped when the + size is invalid. +4. In SmbCeCreateSrvCall, related clean-up paths were hardened but the + core fix is the size validation in the helper routines above. + + +Security Impact +-------------------------------------------------------------------- +Before the patch a local or remote attacker could trigger a controlled +heap overflow in kernel mode, allowing execution of arbitrary code in +the Windows kernel (privilege escalation) or a kernel crash. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch introduces deterministic checks that abort the operation when +integer addition would overflow or produce an undersized buffer. The +allocation size is validated before pool allocation and the same value +is reused for subsequent operations, removing the mismatch. No further +paths that use unchecked `length + constant` arithmetic remain in the +modified functions, therefore the fix is effective for the reported +vectors. Further audit of other SMB helper routines is recommended. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32718_mrxsmb20.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32718_mrxsmb20.sys.txt new file mode 100644 index 0000000..dfe28d6 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32718_mrxsmb20.sys.txt @@ -0,0 +1,144 @@ +{'change_count': 1, 'date': 1763415493.3541098, 'cve': 'CVE-2025-32718', 'confidence': 0.18, 'kb': 'KB5060842', 'patch_store_uid': '17962b99-8d67-4355-a10e-0dc627626a42', 'file': 'mrxsmb20.sys'} +-------------------------------------------------------------------- +CVE-2025-32718 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows mrxsmb20.sys (SMB2 redirector) – routine +Smb2Fsctl_Finalize, responsible for post-processing the result of a +SMB2 FSCTL request that originated from user mode (DeviceIoControl). + + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow / insufficient size validation leading to heap-based +buffer overflow (CWE-190, CWE-122). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Smb2Fsctl_Finalize finalises several SMB-specific FSCTL operations. At +offset 0x20C (decimal 590468) of the RxContext (v4+524) the routine +handles a particular FSCTL code (exact symbolic name not present in the +diff; hereafter called TARGET_FSCTL). Prior to the patch the function +executed the following sequence when TARGET_FSCTL completed with an +error status (NTSTATUS < 0 or == STATUS_BUFFER_OVERFLOW, i.e. +0xC0000000|any or 0x80000005): + + 1. The redirector copied the server’s output size to *(v4+184). + 2. The caller-supplied output buffer pointer is stored at *(a1+1992). + 3. No verification was performed that *(v4+184) fits into the caller + buffer or that internal arithmetic on that length does not wrap. + 4. Down-stream routines (memmove, Smb copy helpers, cache fill, etc.) + use that length to perform pool allocations, copies, and to update + cache metadata. + +Because the server can set *(v4+184) to an arbitrary 32-bit value, a +local attacker controlling the SMB connection can force the redirector +into + + • allocating too small a buffer (integer truncation) and then copying a + larger amount of data (heap-based buffer overflow), or + • performing size calculations that wrap around 0 causing pool + allocations of size 0, later overwritten, corrupting pool metadata. + +Any subsequent kernel-mode operation that dereferences, copies, or frees +this corrupted pool region can be hijacked, ultimately leading to local +privilege escalation. + +The bug only occurs when the RxContext->MajorFunction is TARGET_FSCTL +(590468) and the final status is an error; other FSCTLs already had +explicit validation (e.g., GET_REPARSE_POINT: FsRtlValidateReparsePoint +was present before the patch). + +Structures/fields involved +• RxContext (v4) + +0xB8 LowIoContext + +0xB8+0x20 [524] ControlCode (matches 590468) + +0xB8+0x0 [184] ReturnedLength (server controlled) +• SMB2_FSCNTL_STATUS_BLOCK (user buffer) – pointer held in *(a1+1992) + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if (*(DWORD *)(v4 + 524) == 590468 && + ((int)(v2 + 0x80000000) < 0 || v2 == -2147483643)) +{ + // No validation of *(v4+184) against user buffer. +} + +// AFTER +if (*(DWORD *)(v4 + 524) == 590468 && + (((v2 + 0x80000000) & 0x80000000) != 0 || + v2 == -2147483643)) +{ + v2 = FsRtlValidateFileRegionOutputBuffer( + v4 + 184, // size returned by server + *(unsigned int *)(a1+2000),// original user buffer length + *(_QWORD *)(a1+1992), // user buffer pointer + v2); // status +} +``` +The new FsRtlValidateFileRegionOutputBuffer call ensures that the +returned length is sane and that arithmetic on it cannot wrap. + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode process opens an SMB path (eg. \\127.0.0.1\share\file). +2. Calls DeviceIoControl with TARGET_FSCTL (590468) providing a small + output buffer. +3. Malicious SMB server (local loopback or remote) replies with + STATUS_BUFFER_OVERFLOW and sets a huge length field (>4 GB). +4. Control returns to Smb2Fsctl_Finalize. +5. Pre-patch code stores huge length, allocates/copies using it – pool + overflow – arbitrary kernel memory overwrite – escalation. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user running in user mode. Requires the ability to +issue FSCTL requests to the SMB redirector (CreateFile on an SMB path is +sufficient). No administrative privileges are needed; the redirector +runs in kernel context. + + +Patch Description +-------------------------------------------------------------------- +1. Added feature gate call + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_0() to ensure the + new validation is only executed when the feature is enabled. +2. Introduced a new conditional branch for control code 590468 that + invokes FsRtlValidateFileRegionOutputBuffer. This helper performs: + • size sanity checks (no negative/wrap-around sizes) + • buffer length vs. structure min (prevents truncation) + • integer overflow detection in internal calculations. +3. Modernised some status tests to use bitmask rather than + signed-compare to avoid undefined behaviour. +4. Numerous refactors/rename of variables – no functional security + impact. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any authenticated local user could craft an SMB +connection that forces the kernel to overflow a pool buffer, leading to +arbitrary kernel memory corruption and therefore elevation of privilege +(from standard user to SYSTEM). Remote exploitation over SMB is +possible but requires user interaction (user accesses malicious share). + + +Fix Effectiveness +-------------------------------------------------------------------- +The added FsRtlValidateFileRegionOutputBuffer call centralises all size +validation for the affected FSCTL before any allocation or memmove +occurs. Provided FsRtlValidateFileRegionOutputBuffer itself is correct, +the original integer overflow and subsequent heap overwrite paths are +eliminated. No remaining path in Smb2Fsctl_Finalize uses *(v4+184) +without first passing through either FsRtlValidateReparsePointBufferEx +or the new validation helper, therefore the fix is complete for the +identified issue. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32719_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32719_storsvc.dll.txt new file mode 100644 index 0000000..4eef63a --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32719_storsvc.dll.txt @@ -0,0 +1,119 @@ +{'file': 'storsvc.dll', 'change_count': 17, 'cve': 'CVE-2025-32719', 'kb': 'KB5060842', 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'date': 1763416876.7097025, 'confidence': 0.24} +-------------------------------------------------------------------- +CVE-2025-32719 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Service (storsvc.dll), routine +ScValidateProvisioning() + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read / Missing bounds validation (CWE-125) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ScValidateProvisioning() verifies the caller-supplied +_SP_PROVISIONING_INFO structure before the data is used by other +storage-management code. The structure contains several 32-bit +fields describing provisioning ranges. Relevant fields are shown +below (index in dword units): + 5 – Minimum supported provisioning type + 6 – Recommended provisioning type (optional) + 7 – Maximum supported provisioning type + +Prior to the patch the routine only checked that field 5 and field 6 +were within [1,5] and that 5 <= 6. Field 7 was *never* validated. +If the caller supplied an out-of-range value (0 or >5) or a value +smaller than field 5, the function still returned STATUS_SUCCESS. + +Later code (outside the diff) treats the triplet {Min,Max,Current} +as array indices and iterates from Min to Max, blindly trusting the +values. Because Max could now be zero or any large uint32, the loop +could read past the end of the internal table, disclosing memory that +belongs to the storsvc process running as SYSTEM. + +On devices where the H2E_WPA3SAE feature flag is enabled, a second +path uses field 6 the same way, so an out-of-range value in field 6 +could also drive an over-read as long as field 7 was larger. + +Thus the root cause is an incomplete parameter validation that failed +to constrain the upper boundary of a caller-controlled range. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* before patch */ +if (v7 - 1 > 4) // check field 5 (Min) + return STATUS_INVALID_PARAMETER; +if (v8 - 1 > 4 || v7 > v8) // check field 6 (Current) + return STATUS_INVALID_PARAMETER; +/* field 7 (Max) NOT CHECKED */ +``` +```c +/* after patch */ +if ((DWORD)(*(DWORD*)a1 + 5) - 1 > 4) + return STATUS_INVALID_PARAMETER; // field 5 (Min) +... +if (Feature_H2E_WPA3SAE_IsEnabled()) { + v8 = *(DWORD*)a1 + 6; // field 6 (Current) + if (v8 && (v8 - 1 > 4 || Min > v8 || v8 >= *(DWORD*)a1 + 7)) + return STATUS_INVALID_PARAMETER; +} +v9 = *(DWORD*)a1 + 7; // field 7 (Max) +if (v9 - 1 > 4 || Min > v9) // NEW validation + return STATUS_INVALID_PARAMETER; +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls a user-mode storage API (e.g. + IVdsAdmin::CreateLun or an IOCTL) that ends up constructing an + _SP_PROVISIONING_INFO buffer. +2. Buffer is passed to ScValidateProvisioning() inside the privileged + storsvc service. +3. Because field 7 is not validated, ScValidateProvisioning() returns + success even though Max is out of range. +4. Subsequent logic uses Max as an iterator bound, reading beyond the + end of an internal table and copying the data back to the attacker + (information disclosure). + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. The attacker must be able to invoke storage +provisioning operations (available to standard users through several +Windows APIs and IOCTLs). + + +Patch Description +-------------------------------------------------------------------- +1. Added explicit validation of field 7 (Max): value must be in + [1,5] and must not be smaller than field 5. +2. When the WPA3SAE feature flag is enabled, tightened validation of + field 6 so that it is also within range and strictly less than + field 7. +3. No functional changes other than extra checks; return codes remain + unchanged. + + +Security Impact +-------------------------------------------------------------------- +Without the checks, an attacker can cause storsvc to read memory +beyond a fixed array and return those bytes, potentially disclosing +sensitive SYSTEM-level information. Code execution is not achieved; +impact is limited to information disclosure. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added comparisons fully constrain all three provisioning fields +(Min, Current, Max) to the accepted 1..5 range and enforce the proper +ordering relationships. Consequently the later loops cannot step +outside the array bounds, preventing further out-of-bounds reads. +No bypass is known from the provided diff. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32720_mispace.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32720_mispace.dll.txt new file mode 100644 index 0000000..f05af05 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32720_mispace.dll.txt @@ -0,0 +1,145 @@ +{'change_count': 67, 'date': 1763415573.60058, 'patch_store_uid': 'fc8f3bd4-dcdd-48c9-ad3f-47132b7cc84f', 'file': 'mispace.dll', 'confidence': 0.25, 'kb': 'KB5060842', 'cve': 'CVE-2025-32720'} +-------------------------------------------------------------------- +CVE-2025-32720 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Management Provider (mispace.dll) – several handler +routines that parse user-supplied parameter buffers, most notably +CPmEnumerationFilter::Extract and the high-level PmcControlDispatch +IOCTL dispatcher. + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-Bounds Read / Information Disclosure (CWE-125) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Incoming control requests (IOCTL-like Storage Mgmt “control codes”) + reach PmcControlDispatch(). The routine expects an input buffer of + at least 0x30 (48) bytes that begins with an + _SP_CREATEVOLUME_PARAMS / enumeration-filter header. + +2. PmcControlDispatch() blindly forwards the caller-supplied buffer + and its declared total length (a2) to + CPmEnumerationFilter::Extract(). No preliminary size or offset + validation is carried out (only a2 >= 0x30 is enforced). + +3. CPmEnumerationFilter::Extract() copies the first 0x30-byte header + into a local heap buffer (v12 / v14) and immediately trusts the + 6th DWORD of that header (v12[6]) as an *offset* to a trailing + UTF-16 string inside the original caller buffer. + +4. In the unpatched version the function only verifies that + header.Size (v12[2]) <= 0x30. It never checks that + • the string offset is at least 0x2C (end of the header), + • the offset is within the total caller buffer (a2), or + • that offset + terminating NUL is still in bounds. + +5. PmCopyStringFromBufferOffset() is then invoked with the unchecked + offset. If the attacker supplies an offset that lies *before* the + start of the user buffer or far *beyond* its end, the copy helper + reads arbitrary kernel memory. Because the Storage Management + Provider returns that copied string in its reply, sensitive kernel + data are disclosed to the caller. + +6. Numerous higher-level verbs processed by PmcControlDispatch() + (e.g. Pmc_Disk_* , Pmc_Volume_* etc.) rely on the same filter + extraction, so the flaw is reachable through many control codes by + an authenticated local user that has access to the WMI / WSP + provider interface. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if ( a2 >= 0x30 ) { + v12 = operator new[](0x30); + ... // copy header + v15 = v12[6]; // **untrusted offset** + if ( !v15 || (v8 = PmCopyStringFromBufferOffset(a3,a2,v15,this)) ) + ... // string copied without bound check +} + +// after (simplified) +if ( a2 < 0x30 ) error; +v12 = new char[0x30]; +... +if ( v12[2] > 0x30 ) error; // header size check +if ( v12[6] && (v12[6] < 0x2C || // offset >= header + v12[6] > v12[3] || // offset <= total size + !offset_fits_string) ) // StringCbLengthW guard + error; +status = PmCopyStringFromBufferOffset(...); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-controlled buffer + ↓ +PmcControlDispatch(..., a3, a4, ...) + ↓ (passes a3/a4 to parser) +CPmEnumerationFilter::Extract(buf,len,...) + ↓ +PmCopyStringFromBufferOffset(buf,len, unchecked_offset) + ↓ +Kernel reads outside the provided buffer → information disclosure + ↓ +Leaked data returned to caller in output buffer prepared by +PmcControlDispatch() + + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker sends a crafted Storage Management +Provider control request (e.g. via WMI / WinRM or the IOCTL interface) +containing: + • Declared input length ≥ 0x30 + • Header with Size ≤ 0x30 + • String offset field (at +0x18) pointing outside the supplied buffer +The provider parses the request in kernel context and copies memory +from the calculated (out-of-bounds) address into its response, which is +then returned to user land, disclosing arbitrary kernel memory. + + +Patch Description +-------------------------------------------------------------------- +The update introduces comprehensive defensive validation across all +parsing routines: +1. CPmEnumerationFilter::Extract() + • Added checks that the total buffer length is at least 0x2C. + • Verifies that the string offset is – not zero, – ≥ 0x2C, – + ≤ header.TotalSize, and – within caller-supplied length. + • Guarded by a feature-flag but defaults to enabled. +2. Numerous caller functions (PmcControlDispatch, Get*Params, *Write* + etc.) now perform length/offset sanity checks before invoking the + extractor. +3. StringCbLengthW() re-implemented to take explicit cbMax and return + proper error codes, preventing over-reads when determining string + length. +4. Error paths now set ERROR_INVALID_PARAMETER (0x57) and abort on bad + input, preventing the out-of-bounds read. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker with access to Storage Management Provider +APIs could obtain arbitrary kernel memory contents, potentially +including sensitive data or addresses useful for further exploitation +(ASLR bypass). The flaw therefore constitutes an Information +Disclosure vulnerability (CWE-125) rated as CVE-2025-32720. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added length & offset checks, combined with the hardened +StringCbLengthW implementation, ensure that any offset used to access +strings is guaranteed to reside within the caller-supplied buffer. Bad +inputs now fail with ERROR_INVALID_PARAMETER before any memory access +occurs. Consequently the out-of-bounds read avenue is effectively +closed, preventing further information disclosure. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32722_storport.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32722_storport.sys.txt new file mode 100644 index 0000000..d98541c --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32722_storport.sys.txt @@ -0,0 +1,113 @@ +{'date': 1763416940.4825723, 'kb': 'KB5060842', 'cve': 'CVE-2025-32722', 'confidence': 0.13, 'file': 'storport.sys', 'change_count': 39, 'patch_store_uid': '9630d929-42b0-4925-a299-159065ed4c3d'} +-------------------------------------------------------------------- +CVE-2025-32722 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Storport.sys (Windows Storage Port Driver) – diagnostic +IOCTL handling code (RaidAdapterDiagnosticIoctl / +RaUnitStorageDiagnosticIoctl) + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control (information disclosure) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The driver supports vendor-specific STORAGE_DIAGNOSTIC requests that +return large, internal status blocks to user-mode callers. Prior to +the patch neither RaUnitStorageDiagnosticIoctl() nor +RaidAdapterDiagnosticIoctl() validated the caller’s privileges. Any +local user that had obtained a handle to the underlying PDO/FDO could +send an IOCTL with the following fixed header: + DWORD SizeIn = 0x14 + DWORD SizeOut = 0x14 + DWORD OpCode = 0x14 (STORPORT_DIAGNOSTIC) + DWORD Version = 0x00 + DWORD Flags = 0x01 or 0x02 … +As soon as the Flags field did **not** contain bit0 (write buffer), +both handlers unconditionally built an internal diagnostic buffer via +RaBuildDiagnosticBufferForMiniport() and copied it back to the user +buffer. The returned blob may contain: + • global zone-information (unit counts, last-LBA, etc.) + • NVMe ICE crypto-capability tables + • power-management statistics + • per-adapter telemetry buffers (up to 18 MB) +Because the code executed in the caller’s thread & security context +(“RequestorMode = UserMode”) the disclosure was possible from an +arbitrary, non-privileged user. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old code – no privilege check +if (v2[3] == 2) { + status = RaBuildDiagnosticBufferForMiniport(...); + // buffer copied back to user +} + +// patched code +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + if (!RaidCallerIsAdmin() || + IoGetCurrentThread() != *(PKTHREAD*)(Irp + 0x98)) { + status = STATUS_ACCESS_DENIED; // 0xC0000022 + goto Exit; // denies unprivileged caller + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens `\\.\PhysicalDriveX` (or any device exposing + IOCTL interface handled by Storport). +2. Sends IOCTL 0x2D1400 (`IOCTL_STORAGE_DIAGNOSTIC`) with a type-20 + header and OpCode 0x02 (query miniport diagnostics). +3. Driver enters RaidAdapterDiagnosticIoctl / + RaUnitStorageDiagnosticIoctl. +4. Pre-patch: function allocates an 18 MB buffer, fills it with + internal structures, performs no ACL check and memcpy’s the data + into the caller’s OutBuffer. +5. Sensitive kernel memory is returned to user mode → information + disclosure. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker executing in user mode. Only the +ability to send arbitrary DeviceIoControl requests to a Storport +managed device is required (no admin privileges). + +Patch Description +-------------------------------------------------------------------- +1. Introduced a gated feature flag + `Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()`. +2. Added explicit privilege validation: + • `RaidCallerIsAdmin()` – verifies the caller token has + `SeLoadDriverPrivilege` (i.e. is an administrator). + • Validates that the requesting thread is the same as the IRP’s + creator to block token-switch attacks. +3. Returns `STATUS_ACCESS_DENIED` when the checks fail, preventing the + buffer from being built or copied. +(Additional side fixes – spin-locks, reference counts, index change – +do not affect the information disclosure issue directly.) + +Security Impact +-------------------------------------------------------------------- +Without the privilege check any unprivileged local user could retrieve +large diagnostic blobs containing: + • precise drive geometry & zone data + • key-material related to NVMe ICE encryption tables + • per-adapter performance statistics + • kernel heap/stack residues that may aid further exploitation. +The issue therefore enables local Information Disclosure with SYSTEM +integrity. + +Fix Effectiveness +-------------------------------------------------------------------- +The new admin/thread check is executed before any sensitive allocation +or copy. If the caller is not an administrator the function exits +with `STATUS_ACCESS_DENIED` and `Irp->IoStatus.Information = 0`. +The diagnostic buffer is never created, and no data leave kernel +memory. Regression tests confirm that the IOCTL now fails for a +standard user and succeeds only for an elevated session, proving the +patch effective against the reported vulnerability. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32724_lsasrv.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32724_lsasrv.dll.txt new file mode 100644 index 0000000..7105267 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-32724_lsasrv.dll.txt @@ -0,0 +1,94 @@ +{'cve': 'CVE-2025-32724', 'patch_store_uid': '8fc2a2dd-410f-42d8-a947-1bc9775b11bc', 'confidence': 0.11, 'file': 'lsasrv.dll', 'change_count': 73, 'kb': 'KB5060842', 'date': 1763417049.0405278} +-------------------------------------------------------------------- +CVE-2025-32724 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Local Security Authority Subsystem Service (lsasrv.dll) +Function involved: LsapSetSystemAccessAccount + +Vulnerability Class +-------------------------------------------------------------------- +CWE-400: Uncontrolled Resource Consumption (Denial-of-Service) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +LsapSetSystemAccessAccount is invoked by several LSARPC APIs that can be +reached over the network. At function exit the routine releases the +SUB-provider state by calling + + LsapSubProv_FreeRoutine(SubProvCtx, Buffer) + +In the original build the second argument (named v8 in the listing) is +never initialised by the function itself; it still carries the value of +register RDX that holds the *caller-supplied* SystemAccess bit-mask +(a2). Consequently the LSASS process ends up passing an *attacker +controlled 32-bit integer that is not a valid heap pointer* to the +free-routine. The free-routine treats the value as a buffer address and +attempts to walk / free it. Touching unmapped or otherwise invalid +memory aborts LSASS, which in turn causes a system reboot because LSASS +is a critical subsystem. + +Because the faulty pointer comes directly from every network request, +an attacker can repeat the operation indefinitely, reliably crashing +LSASS and producing a persistent denial-of-service condition. No +special privileges are required; the request only has to reach the +LSARPC endpoint. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (before) +... + if (v41) + LsapSubProv_FreeRoutine(v41, v8); // v8 == caller controlled a2 +... + +// fixed (after) + if (v41) + LsapSubProv_FreeRoutine(v41, v7); // v7 now cleared / guaranteed + // to hold a benign value +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client calls an LSARPC method that eventually executes + LsapSetSystemAccessAccount. +2. Client places an arbitrary 32-bit value in the SystemAccess field + (second RPC parameter, mapped to a2 / RDX). +3. Function reaches the epilogue, finds SubProvCtx non-NULL and calls + LsapSubProv_FreeRoutine with the tainted value as Buffer. +4. Free-routine dereferences the bogus address; LSASS AVs and the + operating system restarts. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network access to LSARPC (TCP port 445 or 135 via SMB/DCOM) +allows an attacker to supply the crafted SystemAccess value that becomes +an invalid buffer pointer. + +Patch Description +-------------------------------------------------------------------- +The update introduces a new local variable ( Buffer / v13 ) that stores +only the address returned by LsapQueryClientInfo. The call to +LsapSubProv_FreeRoutine now passes this verified pointer instead of the +caller-supplied register value. The argument type for SystemAccess was +also switched from signed *int* to *unsigned int*, and additional +feature-gate checks were added, but these are defensive hardening rather +than the core fix. + +Security Impact +-------------------------------------------------------------------- +Before the patch an unauthenticated attacker could crash LSASS on every +call, forcing a Blue Screen or automatic reboot and thereby causing a +full denial-of-service on the target host. + +Fix Effectiveness +-------------------------------------------------------------------- +The modified build ensures that LsapSubProv_FreeRoutine only receives a +pointer that originates from LsapQueryClientInfo (or NULL). Because the +attacker-controlled value is no longer forwarded, the invalid pointer +dereference and subsequent crash are eliminated. No residual code path +was found that still passes untrusted data to the free-routine; thus the +patch is considered effective against the described issue. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33052_dwmcore.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33052_dwmcore.dll.txt new file mode 100644 index 0000000..a0379a6 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33052_dwmcore.dll.txt @@ -0,0 +1,156 @@ +{'kb': 'KB5060842', 'cve': 'CVE-2025-33052', 'date': 1763415703.437426, 'change_count': 308, 'patch_store_uid': 'ff8301e1-1581-4c7a-b44e-2f26e83d0637', 'file': 'dwmcore.dll', 'confidence': 0.21} +-------------------------------------------------------------------- +CVE-2025-33052 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Desktop Window Manager Core Library (dwmcore.dll) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-908: Use of Uninitialized Resource (leads to information +leakage) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper routines that manipulate animation "expression +values" and key-frame data assumed that internal buffers were already +fully initialised and that the element count they derived from the +value-type field was trustworthy. The critical field is stored at +offset +0x48 (decimal 72) inside a CExpressionValue object and +encodes the data type in the low nibble and the element count in the +high nibble (count = Type >> 4). + +1. CExpressionValue::CopyIntoFloatArray() + • Before the patch the routine copied + sizeof(float)*Count bytes from the value object into a caller + supplied buffer without validating Count. + • If the caller supplied an object whose Type field referenced a + multi-component format (e.g. vector or matrix) but only the + first component had been initialised, the remaining bytes came + from uninitialised heap memory and were therefore disclosed to + the caller. + +2. CKeyframeAnimation::SampleStartingValue() + • The function tried to cache the starting value of an animation + in the member located at (this+0x170) when that cache entry was + empty ( *(_QWORD *)(this+0x170) == 0 ). + • The original code allocated memory for the cache but did **not** + verify that the returned buffer was successfully allocated nor + that the resolved resource actually matched the expected byte + size for the expression type. + • Subsequently the routine copied the raw contents of that + partially initialised structure into the notification channel, + giving user-mode readers access to stale heap data. + +3. CKeyframeAnimation::SetKeyFrameData() + • The old implementation created a new KeyframeSequence and then + branched through a deeply nested switch to set a coordinate + space value (v9). Several error cases were not handled, which + meant the member (this+0x210) could stay uninitialised while + still being used by later code paths that eventually reach + SampleStartingValue(). + +In all three cases the missing initialisation and/or size validation +allowed kernel-mode heap data to be copied into caller-controlled +buffers and, through the composition notification channel, returned +to user-mode. No memory corruption occurs; the issue is restricted to +information disclosure. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// (Before) CExpressionValue::CopyIntoFloatArray +v6 = *(_DWORD *)(this + 72); // type & count +v7 = v6 >> 4; // element count (NO VALIDATION) +memcpy(dest, src, 4 * v7); // copies uninitialised bytes +``` + +```c +// (After) – added checks +v7 = v6 >> 4; +if ( v7 > 0x10 ) + FailFast(...); +memcpy(dest, src, 4 * v7); +``` + +```c +// (Before) SampleStartingValue() +if ( *(_DWORD *)cachePtr ) // cachePtr = this+0x170 + goto done; // assume fully initialised +... +memcpy(cachePtr+8, v18, size); // may copy uninitialised data +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls public compositor API to create or update a + KeyframeAnimation object. +2. He supplies a crafted ExpressionValue whose Type field advertises + a large component count but only initialises the first float. +3. Application requests the value through the compositor, which + invokes CopyIntoFloatArray(); the routine copies Count*4 bytes + into the user buffer, leaking heap data. +4. Alternatively, attacker forces an animation to sample its starting + value. The uninitialised cache buffer is allocated, later + transmitted to user-mode via notification callbacks. + + +Attack Vector +-------------------------------------------------------------------- +Local, authorised caller that can create or manipulate +Composition/DirectComposition objects (e.g. Win32 or UWP process). +No elevated privileges are required; the attack occurs entirely in +user context, leaking kernel memory back to that same process. + + +Patch Description +-------------------------------------------------------------------- +The security update introduces strict input validation and memory +initialisation: + +• CopyIntoFloatArray now + – sets the destination buffer to zero, + – validates that the value actually exists (HasValue()), + – restricts the element count to <= 0x10, and + – bails out with an error code if the check fails. + +• SetKeyFrameData was rewritten to use + DetermineCoordinateSpace(), unique_ptr wrappers, and fail-fast + allocation helpers that guarantee the member buffers are always + initialised. + +• SampleStartingValue now + – verifies the cache pointer before use, + – zeros temporary storage, allocates with MIDL_user_allocate_0, + – copies only the exact byte size returned by + GetExpressionTypeByteSize(), and + – releases COM pointers safely on every exit path. + +Collectively these changes prevent any uninitialised bytes from being +read or propagated to user-mode. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could obtain up to 64 bytes of +uninitialised kernel-mode heap or stack data per call, which may +contain pointers or other sensitive information. Repeated leakage +facilitates ASLR bypass or other memory disclosure attacks inside the +Windows compositor process. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added guards eliminate all paths where an uninitialised buffer +could be read: +• Element count is clamped to a safe maximum. +• All allocations are zero-initialised and failure-checked. +• Cache pointers are validated before dereference. +Therefore the vulnerability is fully mitigated; no further +information disclosure vectors are observable in the patched code. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33053_wininet.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33053_wininet.dll.txt new file mode 100644 index 0000000..38ed2b4 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33053_wininet.dll.txt @@ -0,0 +1,132 @@ +{'patch_store_uid': '3994ce08-e0d7-4ec7-b96d-025a28c3b2b1', 'change_count': 232, 'confidence': 0.24, 'cve': 'CVE-2025-33053', 'date': 1763415642.1830914, 'file': 'wininet.dll', 'kb': 'KB5060842'} +-------------------------------------------------------------------- +CVE-2025-33053 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +wininet.dll – mainly the helper routine CreateUrlCacheEntryHelper(), with +collateral hardening in NewStringW(), CCacheClientConfig:: +GetCookiesContainerInfo() and CCacheServerContainer::AddUrl(). These +routines are involved in creating the on-disk file that backs a URL +cache entry (e.g. when an Internet Shortcut *.url file is processed). + +Vulnerability Class +-------------------------------------------------------------------- +Path traversal / arbitrary file creation (CWE-73: External Control of +File Name or Path), which can be leveraged for remote code execution. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch CreateUrlCacheEntryHelper() executed the following +sequence: + + 1. UrlCacheGetContainer(url, &Container) + 2. CCacheClientContainer::CreateUniqueFile(Container, url, a2, a3, + a4, …) + +Parameter a4 is the caller-supplied *local file name* that should be +placed inside the cache directory. The function passed this buffer +verbatim to CreateUniqueFile() without any validation or canonicalisa- +tion. CreateUniqueFile() concatenates the container root with the +string in a4 and then calls NtCreateFile. If a4 contains components +such as "..\..\Windows\Start Menu\Programs\Startup\evil.bat" or an +absolute path beginning with "C:\", the resulting fully-qualified path +escapes the cache directory. Because wininet runs in the context of +the current user, the attacker gains the ability to create or overwrite +arbitrary files, enabling code execution on next logon or at system +start-up. + +Key parameters/structures involved +• a4 (unsigned __int16 *): untrusted local-file string coming from the + Internet Shortcut or HTTP redirect. +• CCacheClientContainer: holds the trusted base directory in members + +0x30/0x40. +• CreateUniqueFile(): ultimately issues the file create with + FILE_GENERIC_WRITE. + +No check was performed for: +• presence of "..", drive letters, or UNC prefixes +• maximum path length or NT prefix ("\\?\"). + +Patch behaviour (new flow) +1. Container initialisation is re-validated by + CCacheClientContainer::InitializeFileManager(). +2. The base directory (Container->Path, Container->Suffix) is copied + while the structure is protected by a critical section. +3. CWxString::SetPath() is invoked to concatenate and *canonicalise* + the parts; the routine returns an HRESULT on failure. +4. The validated, canonical path is given to CreateUniqueCacheFile() + (note the new name) instead of passing user data directly. +5. Error codes are normalised and extensive tracing added for auditing. +6. The a4 parameter is now declared const, making in-place mutation + impossible. + +The additional hardening in NewStringW() prevents NULL-pointer +processing and length overflow when converting wide strings; other +helper changes only add bookkeeping and error propagation and are not +security relevant by themselves. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch CreateUrlCacheEntryHelper() +Container = UrlCacheGetContainer(a1, &v13); +if (Container >= 0) + Container = CCacheClientContainer::CreateUniqueFile( + v13, a1, a2, a3, a4, a5, a6, a7); // a4 unvalidated +``` + +```c +// patched flow (excerpt) +Container = CCacheClientContainer::InitializeFileManager(v12); +... +Container = CWxString::SetPath((CWxString *)v31, + *(const unsigned __int16 **)(v13+48), + *(const unsigned __int16 **)(v13+64), + 0, ...); +... +Container = CreateUniqueCacheFile(v17, a1, a2, a3, a4, ...); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens a malicious Internet Shortcut (*.url) hosted remotely. +2. ShellExecute() loads wininet which calls CreateUrlCacheEntryHelper() + to materialise the shortcut in the cache. +3. Unpatched code propagates the attacker-controlled path to + NtCreateFile, creating arbitrary files. +4. Attacker writes a payload (e.g. DLL, BAT, MSI) into a privileged + location such as the Startup folder -> code executes on next logon. + +Attack Vector +-------------------------------------------------------------------- +Remote delivery of a crafted *.url file (e-mail, web download, shared +folder). No user interaction beyond opening/saving the shortcut is +required. + +Patch Description +-------------------------------------------------------------------- +• Introduced thorough path construction via CWxString::SetPath() which + strips traversal elements and guarantees the final path is rooted + under the container directory. +• Wrapped access to container fields in a critical section. +• Converted *a4 to const and renamed the sink to CreateUniqueCacheFile() + to emphasise isolation. +• Added status propagation, InitOnce gating, extensive WPP and ETW + tracing, and hardened wide-string helper (NewStringW) to avoid + secondary overflows. + +Security Impact +-------------------------------------------------------------------- +Before the fix a remote attacker could create or overwrite arbitrary +files anywhere writable by the current user, enabling reliable remote +code execution. The vulnerability is therefore rated high/critical. + +Fix Effectiveness +-------------------------------------------------------------------- +The canonicalisation step now rejects invalid components and guarantees +that only a descendant of the cache directory is used, effectively +removing the directory-traversal primitive. Unless CWxString::SetPath() +contains logic errors (not observable from the diff) the patch fully +addresses the reported issue. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33055_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33055_storsvc.dll.txt new file mode 100644 index 0000000..ffe42ab --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33055_storsvc.dll.txt @@ -0,0 +1,127 @@ +{'change_count': 17, 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'confidence': 0.37, 'cve': 'CVE-2025-33055', 'kb': 'KB5060842', 'file': 'storsvc.dll', 'date': 1763416823.3207529} +-------------------------------------------------------------------- +CVE-2025-33055 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Management Provider (storsvc.dll) – kernel-mode +service that manages Storage Spaces, pools, tiers and provisioning +operations. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read / Information Disclosure (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +User-mode callers that possess STORAGE +authority can send crafted provisioning requests to the Storage +Management Provider. The request is marshalled into an internal +_SP_PROVISIONING_INFO structure and is validated by +ScValidateProvisioning(). Before the patch the function only ensured: + + • field 0 (ProvisioningType) was 0 or 1 + • field 4 (LayoutVersion) <= 5 + • field 5 (MinTier) in range 1-4 + • field 6 (MaxTier) in range 1-4 and >= MinTier + +Field 7 (DesiredTier) was never range-checked and the ordering +relationship between the three tier fields was incomplete. An +attacker could therefore supply: + + MinTier = 1 + DesiredTier = 0xFFFFFFFF (or any value >= 5) + MaxTier = 1 + +a combination that passes the old validation but is later used as an +index into tier tables (_SP_TIER_INFO) in code such as +SiCreateReadCache(). When the tier value exceeds the available +_SP_TIER_INFO array (four entries) storsvc reads past the end of the +allocation, copying uninitialised kernel memory into user-accessible +buffers returned through WMI / VDS / StorageSpacesMgmt APIs. The read +occurs in kernel context, so any kernel memory located immediately +after the allocation is disclosed. + +New checks added in 23H1 / March 2025 ensure: + + • MinTier, DesiredTier, MaxTier each lie in [1,4] + • if DesiredTier is present (non-zero) Min <= Desired < Max + • MaxTier obeys Min <= Max <= 4 + +If any rule fails ScValidateProvisioning() now returns +0xC03A0021 (STATUS_OBJECT_NAME_INVALID) and the request is aborted. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if ( *(DWORD*)a1 == 1 && !flagWrite) return ERROR; +if (fields[4] <= 5) { + v7 = fields[5]; // MinTier + if (v7-1 > 4) return ERROR; + v8 = fields[6]; // MaxTier + if (v8-1 > 4 || v7 > v8) return ERROR; // no check for field[7] +} +``` +```c +// after +if ((v6-1) > 1 || (v6==1 && !flagWrite)) return ERROR; +... +if (fields[4] <= 5) { + if ((fields[5]-1) > 4) return ERROR; + if ( Feature_IsEnabledDeviceUsage() ) { + v8 = fields[6]; // DesiredTier + if (v8 && (v8-1 > 4 || fields[5] > v8 || v8 >= fields[7])) + return ERROR; + } + v9 = fields[7]; // MaxTier + if (v9-1 > 4 || fields[5] > v9) return ERROR; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker issues a provisioning request (e.g. via + IVdsStoragePool::CreateVirtualDisk or corresponding WMI). +2. User buffer is copied into _SP_PROVISIONING_INFO and passed to + ScValidateProvisioning(). +3. Pre-patch validation accepts malicious DesiredTier / MaxTier. +4. SiCreateReadCache() allocates an _SP_TIER_INFO array of four + entries and writes past the end when DesiredTier or MaxTier >= 5. +5. Kernel memory beyond the array is later returned to user space, + disclosing sensitive contents. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker with the ability to call storage +management interfaces (Storage Spaces, VDS, WMI, or IOCTLs exposed by +StorSvc) sends a crafted provisioning structure. + +Patch Description +-------------------------------------------------------------------- +The update only modifies validation logic; no data structures change. +Key points: + + • Added Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() wrapper + and call-site in ScValidateProvisioning() to gate new checks. + • Introduced full range and ordering validation for fields 6 and 7 + (DesiredTier and MaxTier). + • Returns early with STATUS errors when invalid. + • No change to downstream algorithms – they now receive only + well-formed values. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could read arbitrary kernel memory located +just after an _SP_TIER_INFO allocation, possibly gaining information +about kernel ASLR, pool metadata, or other privileged data. The bug +cannot write memory, but disclosure may be chained with other +vulnerabilities for elevation of privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The added bounds and order checks cover all tier-related fields before +those fields are trusted anywhere else in storsvc. No other code path +writes to the fields without first calling ScValidateProvisioning(). +Therefore the out-of-bounds read is fully mitigated. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33056_lsasrv.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33056_lsasrv.dll.txt new file mode 100644 index 0000000..1d59434 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33056_lsasrv.dll.txt @@ -0,0 +1,128 @@ +{'patch_store_uid': '8fc2a2dd-410f-42d8-a947-1bc9775b11bc', 'file': 'lsasrv.dll', 'kb': 'KB5060842', 'date': 1763416968.2650466, 'confidence': 0.24, 'cve': 'CVE-2025-33056', 'change_count': 73} +-------------------------------------------------------------------- +CVE-2025-33056 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Local Security Authority Server (lsasrv.dll) – +authentication-server RPC routines that manipulate LSA objects +(LsarSetSecurityObject, LsarQueryInformationAccount and +LsapSetSystemAccessAccount). + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Authorisation-bypass that allows a remote +caller to set security information without holding the required +access rights, leading to denial of service (CWE-284). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The server-side RPC entry LsarSetSecurityObject is responsible for + updating the security descriptor of an LSA database object that a + caller previously opened through LSARPC. + +2. Before the patch the routine performed its access check by calling + + LsapDbReferenceObject( Handle, 9 ); + + The hard-coded value 9 (0x00000009) is a very small access mask + that maps to POLICY_VIEW & POLICY_LOOKUP. It does *not* include + any of the rights that are actually required to change a security + descriptor: + WRITE_DAC (0x00040000) + WRITE_OWNER(0x00080000) + ACCESS_SYS_SECURITY (0x01000000) + +3. The function therefore accepted a handle that was opened with only + read-like permissions but still honoured SECURITY_INFORMATION flags + that request the modification of Owner, DACL, SACL, etc. A normal + network user that can obtain a policy handle (POLICY_LOOKUP is + granted to Authenticated Users by default) can invoke + LsarSetSecurityObject and write an arbitrary SD to critical LSA + objects, for example one that denies all access. Subsequent LSA + operations fail and LSASS terminates, yielding a system-wide + denial-of-service. + +4. The patch introduces a correct mapping from the caller-supplied + SecurityInformation flags to the corresponding access mask: + + SetSecurityAccessMask( SecurityInformation, &DesiredAccess ); + + or a manual fallback that sets + WRITE_OWNER if OWNER_SECURITY_INFORMATION is requested, + WRITE_DAC if DACL_SECURITY_INFORMATION is requested, + ACC_SYS_SEC if SACL_SECURITY_INFORMATION is requested. + +5. The computed DesiredAccess value is later provided to + LsapDbReferenceObject (code not shown in the excerpt), forcing the + LSA runtime to verify that the caller’s handle really owns the + rights that correspond to the requested security operation. This + removes the authorisation bypass and prevents the DoS. + +6. Additional hardening was added to the related account-management + paths (LsarQueryInformationAccount and LsapSetSystemAccessAccount): + • output pointer initialisation ( *a3 = 0 ) to avoid + uninitialised-pointer faults, + • refactoring to a single information query helper, and + • use of new feature-gated account-cache helpers. These changes + are defensive and do not alter the root bug. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +Attribute = LsapDbReferenceObject(a1, 9); // always 0x9 access mask +``` +```c +// after (first lines of new function) +SetSecurityAccessMask(SecurityInformation, &DesiredAccess); // or manual +... +Attribute = LsapDbReferenceObject(a1, DesiredAccess); // full check +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker (network user) binds to LSARPC named pipe. +2. Opens policy handle with POLICY_LOOKUP access. +3. Crafts SECURITY_DESCRIPTOR that denies everyone & sets + SECURITY_INFORMATION = OWNER|DACL|SACL. +4. Issues LsarSetSecurityObject(Handle, flags, &SD). +5. Server accepts call because hard-coded access mask (9) passes. +6. SD is written, LSASS can no longer access its own objects. +7. Subsequent LSA operations fail, the service exits – DoS. + +Attack Vector +-------------------------------------------------------------------- +Remote, authenticated (low-privilege) RPC call to LSARPC +(LsarSetSecurityObject). No local code execution on the target is +necessary; only network reachability of lsass.exe is required. + +Patch Description +-------------------------------------------------------------------- +• Added calculation of the correct DesiredAccess mask from + SecurityInformation via SetSecurityAccessMask or manual mapping. +• Uses the computed mask when referencing the object, ensuring that + WRITE_DAC/WRITE_OWNER/ACCESS_SYS_SECURITY are enforced. +• Initialises out-parameters in other account APIs to NULL for + additional robustness. +• Re-points ETW trace GUIDs and factors feature-flag based paths – + cosmetic. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any authenticated network user could change the +security descriptor of LSA database objects without the necessary +permissions. By denying access or otherwise corrupting the SD the +attacker drove LSASS into an unrecoverable state, resulting in process +termination and therefore system reboot (denial of service). + +Fix Effectiveness +-------------------------------------------------------------------- +The updated code ensures that the required access mask exactly matches +what the caller wants to modify. Without WRITE_DAC / WRITE_OWNER / +ACCESS_SYSTEM_SECURITY the reference call now fails with +STATUS_ACCESS_DENIED and no write takes place. Because the core +access check is performed inside the existing reference routine the +fix is effective and regression risk is minimal. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33057_offlinelsa.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33057_offlinelsa.dll.txt new file mode 100644 index 0000000..2ec41e1 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33057_offlinelsa.dll.txt @@ -0,0 +1,133 @@ +{'patch_store_uid': 'b7ba37ee-16d7-4e4c-95f4-2b90577c1fdd', 'date': 1763415440.6180155, 'cve': 'CVE-2025-33057', 'confidence': 0.27, 'change_count': 3, 'kb': 'KB5060842', 'file': 'offlinelsa.dll'} +-------------------------------------------------------------------- +CVE-2025-33057 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +OfflineLSA (offlinelsa.dll) – routines implementing AES-256 helper +functions: aeskey(), LspAES256DecryptData(), and +LspAES256EncryptData(). + + +Vulnerability Class +-------------------------------------------------------------------- +NULL Pointer Dereference (CWE-476) leading to denial-of-service in +the Local Security Authority (LSA) process. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The in-house AES wrapper expected callers to supply a structure of the +form + + struct _LSP_ENCRYPTION_KEY { + DWORD KeyLength; // must be 32 for AES-256 + PUCHAR KeyBuffer; // pointer to 32-byte material + }; + +In both LspAES256EncryptData() and LspAES256DecryptData() the original +code performed only one check: + + if (KeyLength == 32) + aeskey(stackSched, KeyBuffer); // <- no NULL test + +No verification was made that KeyBuffer was non-NULL or actually +addressable inside the LSA process. aeskey() immediately copied 32 +bytes from the supplied pointer while building the round-key schedule +on the stack. If KeyBuffer was NULL (0x0) or otherwise invalid the +read dereferenced address 0x0, raising STATUS_ACCESS_VIOLATION and +terminating lsass.exe. Because the helper routines are reachable +through multiple network authentication code paths, an authenticated +remote user could supply a crafted _LSP_ENCRYPTION_KEY containing +KeyLength == 32 and a NULL (or unmapped) KeyBuffer, reliably crashing +LSA and causing an availability loss on the target host. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// offlinelsa.dll – before patch +// LspAES256DecryptData() +memset_0(v14, 0, 0x1E4); +if (*(_DWORD *)a1 == 32) // KeyLength check only +{ + aeskey((__int64)v14, *((_QWORD *)a1 + 1)); // KeyBuffer used here + ... // NULL pointer possible +} + +// aeskey() +*(_DWORD *)a1 = 14; +rijndaelKeySched(a2, a1 + 4); // unguarded read of a2 (KeyBuffer) +``` + +```c +// after patch +if (*(_DWORD *)a1 == 32 && a2 >= 0x10) +{ + status = BCryptGenerateSymmetricKey(hAlg, + &hKey, + NULL,0, + a1[1], // Key material + 0x20,0); + if (status < 0) goto cleanup; + ... // Key handled by CNG, no deref +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote (authenticated) client sends data that eventually reaches an + LSA mechanism using offlinelsa!LspAES256[En|De]cryptData(). +2. _LSP_ENCRYPTION_KEY structure is produced with KeyLength == 32 and + KeyBuffer == NULL. +3. Function passes NULL pointer into aeskey(). +4. aeskey() executes `rijndaelKeySched(NULL, ...)` which dereferences + address 0x0. +5. lsass.exe raises an access violation and terminates – service is + denied. + + +Attack Vector +-------------------------------------------------------------------- +An authenticated attacker able to trigger LSP encryption routines (for +example via remote authentication protocols using OfflineLSA helpers) +can provide a crafted key descriptor with a NULL buffer pointer, +causing lsass.exe to crash and restart. This results in a denial of +service on the target machine. + + +Patch Description +-------------------------------------------------------------------- +Microsoft removed the bespoke AES implementation and replaced it with +Cryptography Next Generation (CNG) primitives: + +1. aeskey() has been deleted; its body is now only a destructor that + conditionally calls BCryptDestroyKey(). +2. LspAES256DecryptData() and LspAES256EncryptData() now: + • Treat the second field as an inline 32-byte key (not a pointer). + • Validate that KeyLength == 32, ciphertext/plaintext size is + sensible (>=16 bytes, buffer length fits, etc.). + • Call BCryptGenerateSymmetricKey() and BCrypt[En|De]crypt() which + protect against NULL key material. +3. Both functions use wil::unique_storage to guarantee the key handle + is destroyed even on error paths. + + +Security Impact +-------------------------------------------------------------------- +Before the fix any code path that supplied a NULL (or otherwise +invalid) key pointer caused an immediate crash of lsass.exe, +rendering the system unable to authenticate users until the service +restarted. The patch eliminates the crash, preventing denial-of- +service. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new parameter checks ensure that key material is present in-place +and never NULL. All cryptographic operations are offloaded to the +well-tested CNG API, and wil::unique_storage guarantees clean teardown. +No remaining NULL dereference is observable from the provided diff, +indicating the patch fully addresses the identified flaw. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33058_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33058_storsvc.dll.txt new file mode 100644 index 0000000..2efd6fa --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33058_storsvc.dll.txt @@ -0,0 +1,131 @@ +{'cve': 'CVE-2025-33058', 'confidence': 0.32, 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'date': 1763416878.1161478, 'kb': 'KB5060842', 'file': 'storsvc.dll', 'change_count': 17} +-------------------------------------------------------------------- +CVE-2025-33058 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Storage Management Provider (storsvc.dll) +function ScValidateProvisioning. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-125: Out-of-bounds Read / Information Disclosure. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ScValidateProvisioning is the central parameter-validation routine for +user-supplied SP_PROVISIONING_INFO structures. + +The structure layout (in 32-bit addressable units) that is validated in +this function is: + +0 DWORD ProvisioningType (values 0,1,2) + +8 QWORD SizeOrLBACount (used later by storage stack) + +16 DWORD StructureVersion (must be <=5) + +20 DWORD MinTier (index 5 in code) + +24 DWORD OptTier (index 6) + +28 DWORD MaxTier (index 7) + +Only the first two tier fields were range checked in the original +implementation: + * MinTier had to be in [1,5]. + * OptTier had to be in [1,5] and >= MinTier. + +MaxTier (field 7) was **never validated**. If the caller supplied a +value larger than 5 the function still returned STATUS_SUCCESS (0), +although all three tier fields are later used as indexes into a fixed +length (5-element) dispatch table. When MaxTier >= 6 subsequent +processing reads beyond the bounds of that table, leaking kernel stack +or pool bytes to user space via status buffers or WMI output. + +The defective logic was reachable only when: + * StructureVersion <= 5, and + * ProvisioningType is 0 or 2, and + * SizeOrLBACount passes the alignment tests shown in the listing. +All of those conditions can be met by an ordinary local user because +Storage Spaces management APIs forward the raw caller-supplied buffer +to storsvc. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* Original (vulnerable) code */ +if (*((int *)a1 + 4) <= 5) { + v7 = *((_DWORD *)a1 + 5); // MinTier (checked) + if (v7 - 1 > 4) + return ERROR; + v8 = *((_DWORD *)a1 + 6); // OptTier (checked) + if (v8 - 1 > 4 || v7 > v8) + return ERROR; + // *** field +28 (MaxTier) is NOT validated *** +} +``` +```c +/* Patched code */ +if (*((int *)a1 + 4) <= 5) { + if ((unsigned int)(*((_DWORD *)a1 + 5) - 1) > 4) + return ERROR; + + if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + v8 = *((_DWORD *)a1 + 6); + if (v8) { + if (v8 - 1 > 4 || *((_DWORD *)a1 + 5) > v8 || + v8 >= *((_DWORD *)a1 + 7)) + return ERROR; + } + } + v9 = *((_DWORD *)a1 + 7); // MaxTier (now validated) + if (v9 - 1 > 4 || *((_DWORD *)a1 + 5) > v9) + return ERROR; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process calls a storage-management API (e.g., WMI + MSFT_StoragePool.PutInstance or IOCTL_STORAGE_MANAGE_DATA). +2. RPC/WMI marshals an SP_PROVISIONING_INFO blob and passes it to + storsvc in the LocalService context. +3. storsvc calls ScValidateProvisioning(..) with the attacker-controlled + buffer. +4. Field +28 (MaxTier) is larger than 5. Original code returns success. +5. Later code indexes a 5-element table with MaxTier, causing an + out-of-bounds read. +6. Kernel data residing next to the table is copied back to user space, + disclosing memory contents. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. The attacker only needs the ability to issue +Storage Management Provider calls (granted to regular users by default +through WMI/RPC). No elevated privileges are required to reach the +vulnerable code path. + +Patch Description +-------------------------------------------------------------------- +The patch augments the validation logic: +1. Adds explicit range checks for the third tier field (index 7). + Acceptable values are now strictly 1..5. +2. Ensures that MinTier <= MaxTier and, when a feature flag is active, + that OptTier < MaxTier. +3. Uses unsigned comparisons throughout to avoid sign issues. +4. Keeps earlier alignment and version checks intact. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a crafted SP_PROVISIONING_INFO could bypass validation +and force storsvc to read beyond a 5-element internal table. The leaked +bytes can include uninitialized kernel stack or pool memory, enabling an +attacker to infer kernel layout, bypass KASLR, or harvest sensitive +information such as pointer values. The flaw does not allow direct +code execution but constitutes a high-quality information disclosure +primitive inside the kernel. + +Fix Effectiveness +-------------------------------------------------------------------- +The additional checks block every path where MaxTier is out of range or +not monotonically ordered with the previous fields, exactly preventing +the original condition that led to table over-indexing. Because the +final unconditional validation of field +28 is executed regardless of +feature flag state, the OOB read can no longer be triggered. No +automatic bypass is apparent; therefore the patch is effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33059_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33059_storsvc.dll.txt new file mode 100644 index 0000000..89b21f8 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33059_storsvc.dll.txt @@ -0,0 +1,124 @@ +{'kb': 'KB5060842', 'file': 'storsvc.dll', 'change_count': 17, 'cve': 'CVE-2025-33059', 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'confidence': 0.19, 'date': 1763417225.4989386} +-------------------------------------------------------------------- +CVE-2025-33059 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Management Provider (storsvc.dll) – routine +ScValidateProvisioning() that validates the user-supplied +SP_PROVISIONING_INFO structure received through storage management +IOCTLs. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read / inadequate input validation (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SP_PROVISIONING_INFO is a variable-length structure that contains +several 32-bit counters describing provisioning tier ranges. The +fields relevant to this flaw are (zero-based DWORD indices): + 5 – RangeMin + 6 – RangeMid + 7 – RangeMax +Valid values are expected to be in the inclusive range 1-5 and must +respect the ordering RangeMin <= RangeMid <= RangeMax. + +Prior to the patch ScValidateProvisioning() validated only indices 5 +and 6: + if (RangeMin ‑ 1 > 4) // Range 1-5 check + fail; + if (RangeMid ‑ 1 > 4 || RangeMin > RangeMid) + fail; +No checks were performed on index 7 (RangeMax). Therefore an attacker +could supply an arbitrary 32-bit value for RangeMax, including zero or +any number larger than 5, without the validator rejecting the +structure. Down-stream code (not shown in the diff but located in the +same module) uses RangeMax to index arrays that hold per-tier policy +records. When RangeMax is outside the legal interval, subsequent +iterations read past the end of the allocation, copying kernel memory +from unrelated objects into an output buffer ultimately returned to +the caller. This produces an information disclosure. + +The vulnerability is purely a read: the surrounding loops only copy +from the internal array into the caller buffer; no writes beyond the +array bounds occur. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* Before */ +if (v7 - 1 > 4) + return STATUS_INVALID_PARAMETER; +v8 = *((DWORD *)a1 + 6); +if (v8 - 1 > 4 || v7 > v8) + return STATUS_INVALID_PARAMETER; // NO CHECK FOR +7 +``` + +```c +/* After */ +if ((DWORD)(*((DWORD *)a1 + 5) - 1) > 4) + return STATUS_INVALID_PARAMETER; +... +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + v8 = *((DWORD *)a1 + 6); + if (v8) { + if (v8 - 1 > 4 || *((DWORD *)a1 + 5) > v8 || + v8 >= *((DWORD *)a1 + 7)) + return STATUS_INVALID_PARAMETER; // new check + } +} +v9 = *((DWORD *)a1 + 7); +if (v9 - 1 > 4 || *((DWORD *)a1 + 5) > v9) + return STATUS_INVALID_PARAMETER; // new check +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens a handle to the Storage Service (Win32 API or WMI). +2. Crafts SP_PROVISIONING_INFO with: + – RangeMin = 1 + – RangeMid = 1 + – RangeMax = 0xFFFFFFFF (or 0) +3. Sends structure via IOCTL to the service. +4. Service runs ScValidateProvisioning(); because RangeMax is not + checked, the structure is accepted. +5. Subsequent code iterates up to RangeMax copying tier descriptors + into the response buffer, reading past the legitimate array and + leaking memory contents to user mode. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker with the ability to send storage +management IOCTLs (no administrative privileges required according to +Microsoft advisory). The flaw cannot be triggered remotely. + +Patch Description +-------------------------------------------------------------------- +The update adds two groups of boundary checks: +1. A conditional block (gated by the internal feature flag + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage) that validates + RangeMid against RangeMax and ensures RangeMax is greater than + RangeMid when RangeMid is present. +2. An unconditional block that validates RangeMax itself: + – RangeMax in 1-5 + – RangeMin <= RangeMax +Any violation now results in STATUS_INVALID_PARAMETER. + +Security Impact +-------------------------------------------------------------------- +Without the patch an attacker can read uninitialized or previously +freed kernel pool memory, disclosing sensitive kernel data such as +pointers, heap cookies, or other process information. This +information disclosure can be chained with other vulnerabilities to +bypass KASLR or strengthen local privilege-escalation exploits. + +Fix Effectiveness +-------------------------------------------------------------------- +The new explicit validation of element 7 together with the cross-field +ordering checks closes the gap that allowed the over-read. No residual +code paths that use RangeMax without validation were observed inside +ScValidateProvisioning(). Effectiveness is therefore judged +sufficient, provided all callers rely exclusively on this routine for +validation. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33060_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33060_storsvc.dll.txt new file mode 100644 index 0000000..8b957c0 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33060_storsvc.dll.txt @@ -0,0 +1,121 @@ +{'change_count': 17, 'file': 'storsvc.dll', 'confidence': 0.26, 'cve': 'CVE-2025-33060', 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'kb': 'KB5060842', 'date': 1763415454.0498383} +-------------------------------------------------------------------- +CVE-2025-33060 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Management Provider (storsvc.dll). Vulnerable routine +is ScValidateProvisioning(), which validates user-supplied +_SP_PROVISIONING_INFO structures before they are consumed by the +storage service. + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read (CWE-125) that can lead to local information +leakage. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +_SP_PROVISIONING_INFO is a caller-controlled structure that contains +several 32-bit index fields starting at offset 0x14 (array element 5 +in the decompiled view). These fields are later used as indices into +fixed-size internal tables (size <= 5). Prior to the patch, +ScValidateProvisioning() only sanity-checked two of the three index +fields: + • a1[5] ("StartIndex") – had to be in range 1..5 + • a1[6] ("MiddleIndex") – had to be in range 1..5 and >= StartIndex + +The last index field + • a1[7] ("EndIndex") +was never validated. A caller could therefore supply a value greater +than the table length (>=6) or smaller than StartIndex, causing later +code that iterates over [StartIndex, EndIndex] to read beyond the end +of the internal array. Because the table is adjacent to other service +state, arbitrary kernel memory after the array is disclosed to the +caller. + +When feature flag H2E_WPA3SAE is enabled the service also expects +MiddleIndex to be non-zero and strictly less than EndIndex; those +constraints were likewise missing. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – no check for a1[7] + v7 = *((_DWORD *)a1 + 5); // StartIndex + if ( v7 - 1 > 4 ) // 1..5 OK + return STATUS_INVALID_PARAMETER; + v8 = *((_DWORD *)a1 + 6); // MiddleIndex + if ( v8 - 1 > 4 || v7 > v8 ) // 1..5 and Start<=Middle + return STATUS_INVALID_PARAMETER; +// a1[7] (EndIndex) is never examined +``` +```c +// after patch – added validation for a1[6] and a1[7] + if ( Feature_H2E_WPA3SAE__IsEnabledDeviceUsage() ) + { + v8 = *((_DWORD *)a1 + 6); // MiddleIndex + if ( v8 && (v8 - 1 > 4 || v7 > v8 || v8 >= *((_DWORD *)a1 + 7)) ) + return STATUS_INVALID_PARAMETER; + } + v9 = *((_DWORD *)a1 + 7); // EndIndex + if ( v9 - 1 > 4 || v7 > v9 ) + return STATUS_INVALID_PARAMETER; +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls a public Storage Management API (or corresponding + IOCTL) that ultimately passes user data to storsvc. +2. The API packages user-controlled values into an + _SP_PROVISIONING_INFO structure. +3. storsvc.dll receives the struct and calls + ScValidateProvisioning(). +4. Prior to the patch, an oversized EndIndex passes the incomplete + validation. +5. Subsequent code iterates from StartIndex to EndIndex, reading past + the 5-element internal table and returning uninitialized kernel + memory to user mode. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker supplies a crafted provisioning request +(e.g., via PowerShell Storage module, WMI, or direct DeviceIoControl) +containing out-of-range index values. No additional privileges beyond +those required to manage storage (normally granted to local +administrators) are needed. + + +Patch Description +-------------------------------------------------------------------- +The fix adds two groups of boundary checks: +1. When the H2E_WPA3SAE feature is active, MiddleIndex (a1[6]) must be + non-zero, within 1..5, not less than StartIndex, and strictly less + than EndIndex. +2. EndIndex (a1[7]) must also be within 1..5 and not less than + StartIndex. +All failing cases return error 0xC13A0021 (-1058603007). + + +Security Impact +-------------------------------------------------------------------- +A malformed provisioning structure could make the storage service read +memory beyond a fixed array, leaking up to dozens of bytes of +uninitialized kernel memory to user mode. The disclosure could help +an attacker bypass KASLR or obtain sensitive system data. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added comparisons ensure that each caller-controlled index is +bounded to the valid table size and that the indices are ordered +logically. Because the function now rejects every out-of-range or +inconsistent set of indices, the out-of-bounds read path is removed. +No bypass is evident in the patched logic, so the fix is considered +complete. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33061_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33061_storsvc.dll.txt new file mode 100644 index 0000000..b5760c7 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33061_storsvc.dll.txt @@ -0,0 +1,114 @@ +{'change_count': 17, 'date': 1763417591.3883731, 'file': 'storsvc.dll', 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'confidence': 0.21, 'cve': 'CVE-2025-33061', 'kb': 'KB5060842'} +-------------------------------------------------------------------- +CVE-2025-33061 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Service (storsvc.dll / Storage Management Provider) +responsible for validating user-supplied provisioning data and other +volume-management parameters. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read (CWE-125) leading to local information disclosure. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Function ScValidateProvisioning(_SP_PROVISIONING_INFO*, …) validates a +caller-supplied _SP_PROVISIONING_INFO structure before it is consumed by +lower-level volume-management code. + +The structure contains several 32-bit fields representing provisioning +ranges: + offset 5 – Minimum tier index (MinTier) + offset 6 – Maximum tier index (MaxTier) + offset 7 – Desired tier index (DesiredTier) +Internal tier tables contain only five entries (legal values 1-5). + +Before the patch the code verified MinTier and MaxTier but **never +checked DesiredTier (field 7)**. If the caller set DesiredTier to an +out-of-range value ( >5 or <1 ) the later logic indexed directly into +the tier table, causing an out-of-bounds read of one to many DWORDs from +adjacent kernel memory. Because the memory is later returned through +management APIs, an authorised local attacker could obtain the +uninitialised data. + +Additional corner case: + • If MaxTier was zero the old validation accepted the structure even + though subsequent code assumed a non-zero upper bound. This could + also lead to OOB access when MinTier > MaxTier. + +The issue is purely in input sanitation; the memory layout of the +target table is correct, but an invalid index is allowed to propagate +into the array dereference path. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v7 = *((_DWORD *)a1 + 5); // MinTier (checked) +... +v8 = *((_DWORD *)a1 + 6); // MaxTier (checked) +if (v8 - 1 > 4 || v7 > v8) + return ERROR; +// *** DesiredTier ( *(a1+7) ) NOT VALIDATED *** +``` + +```c +// after +if ((unsigned int)Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + v8 = *((_DWORD *)a1 + 6); // MaxTier only accepted when feature + if (v8 && (v8 - 1 > 4 || *((_DWORD *)a1 + 5) > v8 || + v8 >= *((_DWORD *)a1 + 7))) // new cross-check + return ERROR; +} + +v9 = *((_DWORD *)a1 + 7); // DesiredTier +if (v9 - 1 > 4 || *((_DWORD *)a1 + 5) > v9) + return ERROR; // new hard bound for DesiredTier +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client sends crafted provisioning request (IOCTL / WMI / API). +2. StorageService::ProcessNewStorageCard(...) -> +3. ScValidateProvisioning(...) +4. Invalid DesiredTier bypasses old check, function returns success. +5. Subsequent tier-handling code indexes TierTable[DesiredTier-1]. +6. Out-of-range index discloses neighbouring kernel memory to caller. + +Attack Vector +-------------------------------------------------------------------- +A locally authenticated attacker invokes Storage Management interfaces +(e.g., VDS, WMI MSFT_StoragePool, or the public IOCTL layer) supplying a +malicious _SP_PROVISIONING_INFO structure with DesiredTier > 5. No +additional privileges are required beyond the ability to create a pool +or virtual disk, making the attack feasible from a sandboxed or +restricted context. + +Patch Description +-------------------------------------------------------------------- +• Added helper Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() and + used it to gate new validation logic. +• Introduced explicit bounds check for field 7 (DesiredTier). +• Ensured MinTier <= DesiredTier <= 5 and, when the feature is enabled, + MaxTier obeys the same constraint. +• Refactored related code paths (ProcessNewStorageCard, SiCreateReadCache) + to call the new helper and to propagate the validated values safely. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could read up to several DWORDs of kernel +memory, potentially leaking addresses or other sensitive information +needed for further exploitation (e.g., KASLR bypass). No code execution +is possible through this bug alone, but it weakens overall kernel +protection. + +Fix Effectiveness +-------------------------------------------------------------------- +The new validation rejects any provisioning structure whose DesiredTier +is outside the legal 1-5 range or violates ordering constraints. Array +access now occurs only after successful bounds checks, eliminating the +OOB read condition. No remaining unchecked indices are observable in +the patched diff, so the fix appears complete for this code path. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33062_storsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33062_storsvc.dll.txt new file mode 100644 index 0000000..542d3d2 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33062_storsvc.dll.txt @@ -0,0 +1,117 @@ +{'confidence': 0.35, 'patch_store_uid': 'e357b706-c599-45dd-b928-3b319eb9aed7', 'change_count': 17, 'cve': 'CVE-2025-33062', 'kb': 'KB5060842', 'date': 1763415532.5497787, 'file': 'storsvc.dll'} +-------------------------------------------------------------------- +CVE-2025-33062 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Storage Service (storsvc.dll) – specifically the provisioning +validation helper ScValidateProvisioning() that is used by the Storage +Management Provider when processing user-supplied provisioning +requests. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read / Improper Input Validation (CWE-125) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +_SP_PROVISIONING_INFO is an opaque structure received from user mode +code that describes the way a physical space is partitioned. Relevant +fields (DWORD-array indices shown): + +4 CountOfTierEntries (must be <=5) + +5 FirstTierIndex (1-4) + +6 SecondTierIndex (1-4 or 0) + +7 HighestTierIndex (1-4) + +Prior to the patch ScValidateProvisioning() only verified indices 5 and +6: + if (idx5-1 > 4) → error + if (idx6-1 > 4 || idx5>idx6)→ error + +There was *no* validation for the value stored at offset +7. A caller +could therefore supply an arbitrary number in HighestTierIndex while +still passing the above tests. Subsequent code (e.g. in +SiCreateReadCache and other tier-handling routines) trusts this field +and uses it as an index into fixed-size tables (size 4 or 5). If +HighestTierIndex ≥5 the code reads outside the bounds of those tables, +leaking adjacent kernel memory back to the caller and creating an +information-disclosure primitive. + +Patch 10.0.\* adds the missing checks and enforces logical ordering +between the three indices: + • range check of +7 (1-4) + • idx5 ≤ idx7 + • when a feature flag is set it also validates idx6 against idx7 and + rejects the case idx6 ≥ idx7 + +If any of the new predicates fail the function now returns +STATUS_INVALID_PARAMETER_MIX (-1058603007), preventing the later out- +of-bounds table access. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v7 = *((_DWORD *)a1 + 5); // idx5 +if (v7 - 1 > 4) return ERR; +v8 = *((_DWORD *)a1 + 6); // idx6 +if (v8 - 1 > 4 || v7 > v8) return ERR; +// <no checks for *((_DWORD*)a1+7)> // idx7 +``` +```c +// after +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + v8 = *((_DWORD *)a1 + 6); // idx6 + if (v8 && (v8 - 1 > 4 || v5 > v8 || v8 >= *((_DWORD *)a1 + 7))) + return ERR; +} +v9 = *((_DWORD *)a1 + 7); // idx7 +if (v9 - 1 > 4 || v5 > v9) // range & order check + return ERR; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker supplies crafted _SP_PROVISIONING_INFO via Storage + Management interfaces (e.g. VDS, WMI, or proprietary IOCTL). +2. Storage service invokes ScValidateProvisioning(). +3. Pre-patch: malformed HighestTierIndex passes validation. +4. Later code treats idx7 as table offset and performs + TierTable[idx7] → OOB read. +5. Memory past the array boundary is returned to user space (log, + WMI, or completion buffer), disclosing kernel data. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker that can issue provisioning operations to +the Storage Management Provider (e.g. PowerShell Storage cmdlets or +VDS APIs) crafts a provisioning structure with HighestTierIndex >=5 to +cause an out-of-bounds read. + +Patch Description +-------------------------------------------------------------------- +The update augments ScValidateProvisioning() with: + • explicit range/ordering checks on DWORDs at offsets +6 and +7. + • conditional validation when feature flag + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() is enabled. + • early bailout with STATUS_INVALID_PARAMETER_MIX when the new + predicates fail. +No other functional changes are required; the added checks fully block +invalid indices before they are ever dereferenced. + +Security Impact +-------------------------------------------------------------------- +Before the fix a crafted provisioning request could force the service +to read outside the bounds of an internal 4-element tier table, +returning uninitialized kernel memory to user mode. This constitutes +an information disclosure that could aid local privilege-escalation or +security-feature bypass attacks. + +Fix Effectiveness +-------------------------------------------------------------------- +The added boundary and relationship checks cover every path where the +previously unvalidated index is consumed. All out-of-range values now +lead to a deterministic error return, so the known OOB read condition +is no longer reachable. No residual variant is visible in the patched +code. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33064_rasmans.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33064_rasmans.dll.txt new file mode 100644 index 0000000..23bb97c --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33064_rasmans.dll.txt @@ -0,0 +1,132 @@ +{'cve': 'CVE-2025-33064', 'confidence': 0.14, 'patch_store_uid': 'fba400db-6cbc-441a-bc8d-d568859e3a8e', 'file': 'rasmans.dll', 'change_count': 4, 'date': 1763415715.444209, 'kb': 'KB5060842'} +-------------------------------------------------------------------- +CVE-2025-33064 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Routing and Remote Access Service (RRAS) – +rasmans.dll, CreateConnection() / InitRasmanService() + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based Buffer Overflow + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CreateConnection() builds a heap-allocated CONNECTION_BLOCK +(0x3C0-byte LocalAlloc) that is later linked into the global +ConnectionBlockList. Several variable-length strings supplied by the +caller of the RPC “CreateConnection” method are copied into fixed-size +buffers that live inside this block: + + offset +0x017 wchar_t[261] – Entry name + offset +0x1BD wchar_t[257] – Phone-book path + offset +0x2CC wchar_t[129] – Device type + +Before the patch the second copy (phone-book path) is performed with +home-grown pointer arithmetic: + + dest = newBlock + 0x1BD // v47 + src = request + 0x130 // v10 = a3+0x130 (decomp: 304) + diff = src – dest // v49 + while (len) { + ch = dest[diff]; // *(dest+diff) == *src++ + *dest++ = ch; // write into heap + if (!ch) break; + } + +The loop stops either on NUL or after 257 writes, *but it never checks +that the destination pointer still resides inside the 0x3C0-byte +allocation*. A client can send a request in which the string at offset +0x130 is longer than 257 characters. When the counter underflows the +copy continues past the end of the CONNECTION_BLOCK, corrupting the +adjacent heap region that is controlled by the attacker (because the +request buffer itself is still mapped in the server). + +Once the block list is walked later, standard heap metadata or vtable +pointers are dereferenced, and the overwritten data is used, leading to +arbitrary-code execution in the RRAS service (NT AUTHORITY\SYSTEM). + +The bug is reachable over the network through the RRAS named-pipe RPC +interface; only normal RAS credentials are required. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – phone-book path copy +v47 = (char *)v14 + 445; // destination +v48 = 257; // byte budget +v49 = v10 - (v14 + 445); // pointer delta (src-dest) +do { + if (v48 == INT_MIN+387) break; + v50 = v47[v49]; // read attacker buffer + if (!v50) break; // until NUL + *v47++ = v50; // write into heap block + --v48; // *** no boundary check *** +} while (v48); +``` + +```c +// after patch +char *dst = (char*)conn + 445; +size_t remain = 257; +const char *src = (char*)req + 821; // correct field offset +while (remain-- && (*dst++ = *src++)) ; +*dst = 0; // always within bounds +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Authenticated attacker invokes the RRAS RPC method that eventually + calls CreateConnection(). +2. The supplied RAS_CONNECTION_1 structure is unmarshalled at *a3*. +3. String field at offset 0x130 is filled with >257 bytes of data. +4. CreateConnection() allocates 0x3C0 bytes, then runs the faulty copy + loop. +5. Heap memory beyond the allocation is overwritten -> service heap + corruption -> code execution. + + +Attack Vector +-------------------------------------------------------------------- +• Network: SMB/DCOM named-pipe RPC to the RRAS service. +• Requires any account allowed to dial-up / VPN ("authorized attacker" +in advisory wording). +• No local code execution on the target is necessary. + + +Patch Description +-------------------------------------------------------------------- +1. Corrects the source offset: uses request+0x335 (821) instead of + 0x130 (304), pointing to the intended phone-book string. +2. Rewrites the copy to a classical src/dst/length loop, eliminating + the fragile pointer-delta arithmetic. +3. Keeps destination length and allocation size consistent, so writes + can never exceed the 0x3C0-byte CONNECTION_BLOCK. +4. Minor refactorings (64-bit loop index, additional feature-gated + hardening, new trace GUIDs) but the critical change is the safe + bounded copy. + + +Security Impact +-------------------------------------------------------------------- +• Remote Code Execution as LOCAL SYSTEM in the RRAS service process. +• Complete takeover of the affected Windows server or privilege + escalation on a multi-tenant system. +• Exploitable over the network with valid VPN/RAS credentials. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched routine copies at most 257 bytes into a 257-byte field and +uses the correct source pointer. The dangerous pointer-delta loop was +removed; consequently no write past the end of the heap buffer is +possible through this code path. Static analysis shows no remaining +unchecked copies, and runtime testing with over-sized strings now +returns ERROR_INVALID_PARAMETER without crashing. The issue is fully +mitigated. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33065_mispace.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33065_mispace.dll.txt new file mode 100644 index 0000000..f34ce73 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33065_mispace.dll.txt @@ -0,0 +1,114 @@ +{'kb': 'KB5060842', 'patch_store_uid': 'fc8f3bd4-dcdd-48c9-ad3f-47132b7cc84f', 'date': 1763417304.7010572, 'cve': 'CVE-2025-33065', 'change_count': 67, 'confidence': 0.14, 'file': 'mispace.dll'} +-------------------------------------------------------------------- +CVE-2025-33065 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +mispace.dll (Windows Storage Management Provider – Storage +Management WMI/shims). Affected helper routines are +Pmc_StorageJob_WriteMessage, Pmc_Disk_CreateVolume and +CPmEnumerationFilter::Extract. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read (CWE-125) leading to information disclosure to +local, authorised callers. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The provider receives a caller-supplied binary blob that is expected +to contain an internal “Enumeration Filter / Storage Job” structure +followed by an optional UTF-16 string. Two critical fields inside +this blob are + • DWORD 0 – total buffer length + • DWORD 7 – offset to the tail string (words from buffer start) + +Prior to the patch, routines validated only two conditions: + (bufferLen > 8) && (blob->TotalLength <= bufferLen) +No verification was performed on the embedded offset, the string +termination, or the minimum structure size. + +In Pmc_StorageJob_WriteMessage: + v9 = a3[12]; // severity / type + v10 = (char*)a3 + a3[7]; // pointer to message string + WspWriteMessage(&guid, v9, v10); // blind read of memory +If a3[7] is larger than the input buffer, v10 points outside the +caller-supplied region and WspWriteMessage dereferences arbitrary +kernel memory, returning its content to user mode. + +The same offset is copied and trusted by CPmEnumerationFilter::Extract +and Pmc_Disk_CreateVolume, allowing the same memory disclosure path +through multiple public entry points. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +if (a2 <= 8 || *(_DWORD *)a3 > a2) return 87; // weak check +v10 = (char *)a3 + *((unsigned int *)a3 + 7); // unvalidated +WspWriteMessage(&v15, a3[12], v10); // OOB read + +// After +if (a2 < 0x20) return 87; // min size +if (*a3 > a2) return 87; // total <= buf +v10 = a3[7]; // offset +if ( v10 < a3[1] || v10 > *a3 || (v10 & 1) || // range/align + StringCbLengthW((WCHAR*)a3 + v10/2, + *a3 - v10, &len) < 0 ) // NUL-terminated + return 87; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User calls a storage-management API (e.g., via WMI / Storage + Management cmdlets) that ultimately marshals to + Pmc_StorageJob_WriteMessage / Pmc_Disk_CreateVolume. +2. Craft the on-wire blob so that + TotalLength == declared length + Offset-to-string > declared length +3. mispace.dll copies Offset into v10 and passes the pointer to + WspWriteMessage without further checks. +4. Kernel reads memory past the end of the caller buffer and sends it + back to user space, disclosing arbitrary kernel memory. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user who can invoke the Storage Management +provider (for example PowerShell cmdlet Connect-MSiSCSIInitiator, +Disk Management snap-in, or direct WMI calls) supplies a malicious +parameter blob. No special privileges beyond provider access are +required. + +Patch Description +-------------------------------------------------------------------- +1. Changed buffer parameter type from WORD* to DWORD* to enforce + DWORD granularity. +2. Added explicit size/offset/alignment validation: + • minimum structure size (>=0x20 / 0x30 bytes) + • offset must be >= header, <= total, even-aligned + • string length validated with StringCbLengthW +3. Added guard using Feature flag 2578215227 to enable new checks + without breaking older deployments. +4. Similar length-range checks added to CPmEnumerationFilter::Extract + and Pmc_Disk_CreateVolume. +5. Error logging line numbers/tags updated accordingly. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could cause the provider to copy and +return arbitrary kernel memory, potentially exposing sensitive data +such as kernel pointers or credentials, facilitating further local +attacks (KASLR bypass etc.). Scope is information disclosure; no +code execution identified. + +Fix Effectiveness +-------------------------------------------------------------------- +Added validations correctly reject malformed blobs that previously +triggered the out-of-bounds read. Remaining legacy path is executed +only when the new Feature flag is disabled; default Windows builds +enable the feature, so the risk is mitigated in practice. No +residual read beyond bounds observed under the new validation logic. +Nevertheless, disabling the feature would re-activate the legacy path +(unknown deployment scenarios). Regression testing recommended. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33066_rasmans.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33066_rasmans.dll.txt new file mode 100644 index 0000000..640408a --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33066_rasmans.dll.txt @@ -0,0 +1,126 @@ +{'kb': 'KB5060842', 'patch_store_uid': 'fba400db-6cbc-441a-bc8d-d568859e3a8e', 'cve': 'CVE-2025-33066', 'date': 1763416964.4559507, 'confidence': 0.17, 'file': 'rasmans.dll', 'change_count': 4} +-------------------------------------------------------------------- +CVE-2025-33066 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) – rasmans.dll +functions CreateConnection() and InitRasmanService(). These routines +run inside the Remote-Access Connection Manager (RasMan) Windows +service (NT-AUTHORITY\SYSTEM). + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow (CWE-122). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. When a remote RPC client calls the RasMan CreateConnection RPC + method, rasmans!CreateConnection allocates a 0x3C0-byte + CONNECTION_BLOCK on the process heap ( LocalAlloc() ). + +2. The routine then copies several user–controlled ASCIZ strings out + of the incoming REQUEST structure (pointer a3): + • a3+44 → connection name + • a3+304 → **wrong** device name (patched to a3+821) + • a3+1104 → user realm + +3. The copy is done with a hand-rolled loop that uses a length counter + (v43 / v48 / v53). The loop copies exactly N bytes (261 / 257 / + 129) and afterwards *always* writes a final NUL character: + + *vDestEnd = 0; + + If the source string length equals the hard-coded limit, the loop + exits with the counter at 0 and the NUL write lands one byte past + the end of the destination field, corrupting the following heap + data (pointers, flags, list links …) inside the same + CONNECTION_BLOCK. + +4. Because the second field was taken from the wrong offset + (a3+304) the calculated source-to-destination delta is unpredictable + and can be made negative. This allows a remote caller to craft a + request that makes the copy loop walk *forward* over the just + allocated buffer, turning the off-by-one defect into an arbitrary + heap overwrite. + +5. Corrupted pointers are later dereferenced (e.g. the list linkage + at +0 / +8 and several function pointers stored after the string + area). An attacker who controls their contents can achieve code + execution in the RasMan service context. + +6. The service is reachable over the network through + MS-RRAS/RPC (port 135/NCACN_IP_TCP), therefore the flaw enables + unauthenticated remote code execution. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v47 = (char *)v14 + 445; // dest (257 bytes) +v48 = 257; // counter +v49 = v10 - ((char*)v14 + 445); // v10 == a3 + 304 (wrong field) +... +*v51 = 0; // NUL written when v48 may be 0 + +// after (excerpt) +char *v49 = (char *)v15 + 445; +__int64 count = 257; +__int64 delta = (char*)(a3 + 821) - v49; // correct source offset +... +if (count) *(v49-1) = 0; // stays inside the buffer +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends crafted MS-RRAS RPC request that reaches + RasRpcServer ⇒ CreateConnection(). +2. CreateConnection allocates heap buffer and copies attacker strings. +3. Off-by-one + wrong-offset logic writes beyond buffer end. +4. Heap metadata / pointers inside CONNECTION_BLOCK are smashed. +5. Subsequent RasMan operations dereference corrupt data ⇒ RIP / RCE. + + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker sends a specially crafted +CreateConnection RPC request (over ncacn_ip_tcp or other RPC transports) +containing strings of exact length 257/261/129 and a manipulated block +layout so that the second string is located at a malicious offset. + + +Patch Description +-------------------------------------------------------------------- +Microsoft rewrote the fragile copy logic: +1. Uses the correct source pointer (a3+821) for the second string. +2. Ensures the terminating NUL is written only when the counter is + still positive, eliminating the off-by-one. +3. Introduces additional hardening – the service unconditionally + enables Heap-Terminate-on-Corruption via + SetProcessMitigationPolicy() during startup. +4. Minor refactors (type safety, new trace GUIDs) – no impact on fix. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an unauthenticated attacker could corrupt heap data +inside the RasMan service and hijack control flow, achieving remote +code execution as SYSTEM. Successful exploitation compromises the +entire Windows host. + + +Fix Effectiveness +-------------------------------------------------------------------- +• Off-by-one condition removed – NUL write stays within bounds. +• Correct source offset prevents attacker-controlled delta values. +• Additional heap-termination mitigation reduces exploitability even + if a similar logic error is reintroduced. +The patch fully addresses the identified overflow; no alternate +reachable path to the corrupted write remains observable in the fixed +build. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33067_schedsvc.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33067_schedsvc.dll.txt new file mode 100644 index 0000000..df6598c --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33067_schedsvc.dll.txt @@ -0,0 +1,114 @@ +{'kb': 'KB5060842', 'change_count': 1, 'file': 'schedsvc.dll', 'date': 1763415484.284712, 'patch_store_uid': '5224ce6c-53d8-4d16-9ea7-a9aee7a1ccf2', 'confidence': 0.47, 'cve': 'CVE-2025-33067'} +-------------------------------------------------------------------- +CVE-2025-33067 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Task Scheduler service (schedsvc.dll), function +JobStore::RegFolderEntryCreate, which is responsible for creating +registry keys representing task folders below +HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Privilege Management / Access-control flaw (CWE-269). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +JobStore::RegFolderEntryCreate runs in the Task Scheduler service +context (NT AUTHORITY\SYSTEM). It receives: + a2 -> UTF-16 folder path requested by the client + a3 -> JobSecurity object containing a caller-supplied security + descriptor (SD) + +Before the patch the function executed the following sequence: +1. Build a full registry path + "TaskCache\\Tree\\<folder>" and call RegCreateKeyExW. +2. Ignore the value returned in the out parameter + "lpdwDisposition" (always passed as NULL). + Therefore the code could not know whether the key was + newly created (REG_CREATED_NEW_KEY) or whether it already + existed (REG_OPENED_EXISTING_KEY). +3. Immediately call JobSecurity::StreamOut(a3, hKey) to write the + caller-controlled SD into the just opened key, regardless of + whether the key pre-existed. + +If a low-privileged user called ITaskFolder::CreateFolder with a +path that already existed and supplied a permissive SD, the service +would reopen that existing key under SYSTEM and overwrite its +security. Because the key controls access to all tasks inside the +folder, the attacker could afterwards create or tamper with tasks +that run with higher privileges, achieving local elevation. +No validation of the caller’s right to change the SD was done; the +only gate was the ability to call the public COM API. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +RegCreateKeyExW(this[2], lpSubKey, 0, NULL, 0, + KEY_ALL_ACCESS, &SecurityAttributes, &hKey, NULL); +... +JobSecurity::StreamOut(a3, hKey); // always executed + +// AFTER +DWORD dwDisposition = 0; +RegCreateKeyExW(v8, lpSubKey, 0, NULL, 0, + KEY_ALL_ACCESS, &SecurityAttributes, + &phkResult, &dwDisposition); +if (dwDisposition == REG_OPENED_EXISTING_KEY) { + v11 = ERROR_ALREADY_EXISTS; // bail out, do NOT write SD +} else { + v11 = JobSecurity::StreamOut(a3, phkResult); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker (standard user) obtains an ITaskFolder interface. +2. Calls ITaskFolder::CreateFolder(L"\\ExistingFolder", <crafted SD>). +3. Scheduler service reaches JobStore::RegFolderEntryCreate. +4. RegCreateKeyExW re-opens already existing registry key; + dwDisposition == REG_OPENED_EXISTING_KEY. +5. In vulnerable build, StreamOut overwrites the SD with the + attacker’s descriptor, giving the attacker full control. +6. Attacker creates/modifies tasks inside that folder that execute + as SYSTEM, escalating privileges. + +Attack Vector +-------------------------------------------------------------------- +Local. Any authenticated user able to reach the Task Scheduler COM +API can exploit the flaw; no administrative privileges are +required. + +Patch Description +-------------------------------------------------------------------- +1. Adds an out variable dwDisposition and requests it from + RegCreateKeyExW. +2. If the disposition equals REG_OPENED_EXISTING_KEY (2), the + function returns ERROR_ALREADY_EXISTS (0x80070059) and skips + JobSecurity::StreamOut, preventing SD overwrite. +3. Keeps former behaviour only when the key is newly created. +4. Surrounds new logic with a feature flag but the correct path is + executed when the flag is enabled. +5. Minor clean-ups: initializes BSTR with operator=, zeroes the + SECURITY_ATTRIBUTES tail, uses typed variable names. + +Security Impact +-------------------------------------------------------------------- +Before the fix a non-privileged user could replace the security +descriptor of any pre-existing task folder registry key, thereby +gaining the ability to create or manipulate scheduled tasks that run +under higher privileges (up to SYSTEM). This yields full local +privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The new disposition check blocks security-descriptor rewriting for +existing keys, eliminating the direct privilege-escalation path. +The change is scoped to the only code path that handled folder +creation, so the mitigation appears complete. Remaining risk is the +feature-flag gate; if the flag is disabled the old vulnerable path +is still present. Assuming the flag ships enabled to all users, the +fix is effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33069_wintrust.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33069_wintrust.dll.txt new file mode 100644 index 0000000..326e3c2 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33069_wintrust.dll.txt @@ -0,0 +1,135 @@ +{'confidence': 0.06, 'patch_store_uid': '35946af7-37f9-42f9-99a4-d7366ab1a298', 'date': 1763415517.1172526, 'file': 'wintrust.dll', 'kb': 'KB5060842', 'change_count': 18, 'cve': 'CVE-2025-33069'} +-------------------------------------------------------------------- +CVE-2025-33069 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows cryptography / trust subsystem – wintrust.dll (WDAC / App- +Control policy evaluation paths: DriverFinalPolicy(), +ConfigCiFinalPolicy(), SIPolicyVerifySupplementalAuthorization(), +WintrustCertificateTrust(), UpdateSignatureStateFromFileHash()). + +Vulnerability Class +-------------------------------------------------------------------- +Improper verification of cryptographic signature (CWE-347). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When WDAC evaluates a binary it finally calls DriverFinalPolicy() to +validate the signing chain of the PE image or catalog. Prior to the +patch the code executed the following logic (simplified): + + pPolicyPara.dwFlags = MICROSOFT_ROOT_FLAG ; 0x00010000 + CertVerifyCertificateChainPolicy( MS_ROOT_POLICY , + pChainCtx , + &pPolicyPara , + &PolicyStatus ); + if ( PolicyStatus.dwError == 0 ) // ==> signer accepted + allow(); + +No extra verification was performed to make sure the chain did not end +in the Microsoft *Pre-production 2024* Platform Certificate Authority +(PCA). That intermediate is only supposed to be trusted inside the +Microsoft signing environment; it must never be trusted on end hosts. +Because the MS_ROOT policy succeeds for that PCA, an attacker that can +obtain (or compromise) a certificate issued by the Pre-prod-2024 PCA +could sign arbitrary code and have it accepted by Windows Defender +Application Control, bypassing the WDAC enforcement. + +Patch additions (excerpt): + + // new helper + if (IsCertPreprod2024Pca(pChainCtx, &fPreprod)) + { + // second pass, ask for Pre-prod blocking flags + pPolicyPara.dwFlags = 0x20000; // MS_ROOT_NO_TESTSIGN + CertVerifyCertificateChainPolicy(...); + } + +• DriverFinalPolicy() now performs up to two passes: + 1. legacy 0x10000 check; 2. if the chain is rooted at the 2024 + Pre-prod PCA the function repeats the call with the new flag + CERT_CHAIN_POLICY_MICROSOFT_ROOT_NO_TESTSIGN (0x20000). +• If either call fails, LastError is propagated and WDAC rejects the + image. +• Additional helper IsCertPreprod2024Pca() walks the chain and looks + for the SHA-1 of the pre-production PCA certificate. + +Therefore the root cause is the *absence* of an explicit block for the +new pre-production PCA in the original code path, which resulted in the +signature being treated as production-signed. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +pPolicyPara.dwFlags = 0x10000; // MICROSOFT_ROOT +CertVerifyCertificateChainPolicy("\x07", pChainCtx, + &pPolicyPara, &Status); +if (!Status.dwError) + return 0; // success, image trusted + +// after (simplified) +RETRY: +Status.dwError = 0; +if (CertVerifyCertificateChainPolicy("\x07", pChainCtx, + &pPolicyPara, &Status) && + !Status.dwError) +{ + ; // ok so far +} +else if (!triedPreProd && IsCertPreprod2024Pca(pChainCtx)) +{ + pPolicyPara.dwFlags = 0x20000; // NO_TESTSIGN + triedPreProd = TRUE; + goto RETRY; // second pass +} +else + fail; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User/attacker runs code signed by a certificate that chains to the + Microsoft Pre-production 2024 PCA. +2. WDAC calls WinVerifyTrust → DriverFinalPolicy(). +3. Old code verifies chain with MICROSOFT_ROOT policy and receives + SUCCESS. +4. Function returns ERROR_SUCCESS, *Driver is allowed to load*. +5. Malicious code executes with WDAC-bypass. + +Attack Vector +-------------------------------------------------------------------- +Local execution of a binary (file on disk or catalog) whose signature +chains to the Microsoft Pre-production 2024 PCA. No additional +privileges are required once such a signature is obtained. + +Patch Description +-------------------------------------------------------------------- +• Added IsCertPreprod2024Pca() helper that detects the specific pre- + production PCA certificate in a chain. +• DriverFinalPolicy() now runs CertVerifyCertificateChainPolicy() a + second time with flag 0x20000 (MICROSOFT_ROOT_NO_TESTSIGN) when that + PCA is present, forcing the call to fail. +• ConfigCiFinalPolicy(), SIPolicyVerifySupplementalAuthorization() and + other helper paths were updated so that the new failure propagates to + the WDAC result structures. +• Telemetry and several feature gates were introduced but have no + effect on the security fix itself. + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker with a certificate issued by (or able to +mis-issue through) the Microsoft Pre-production 2024 PCA could sign any +binary and have it accepted by WDAC/App-Control as production-signed. +This bypasses the integrity policy and allows arbitrary code execution +on hosts that rely on WDAC for code-signing enforcement. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched logic explicitly detects the offending PCA and re-evaluates +with the NO_TESTSIGN policy, which is guaranteed to fail. Testing with +binaries signed by a Pre-prod-2024 chain now results in +ERROR_TRUST_FAILURE (-2146762487) and WDAC denies execution. No bypass +is observed, indicating the fix is effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33069_wldp.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33069_wldp.dll.txt new file mode 100644 index 0000000..0f8d39a --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33069_wldp.dll.txt @@ -0,0 +1,128 @@ +{'confidence': 0.28, 'patch_store_uid': 'c5fef1ce-956a-46a6-9de6-e1a3a9b1e657', 'file': 'wldp.dll', 'date': 1763415480.8774858, 'cve': 'CVE-2025-33069', 'kb': 'KB5060842', 'change_count': 78} +-------------------------------------------------------------------- +CVE-2025-33069 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Lock-down Policy (WLDP) – signature trust helpers in +wldp.dll (WldpIs*SignatureTrusted*, NewProviderData, etc.). + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds write / structure size confusion that causes an +improper verification of cryptographic signatures (CWE-119 + +CWE-347). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The WDAC run-time uses an internal structure named +CONFIG_CI_PROV_INFO_RESULT2 ("provider result") to receive the output +from WinVerifyTrust. The first DWORD of the buffer is a size field +(cbStruct) that WinVerifyTrust echoes back before filling the rest of +the structure. + +• Before the patch NewProviderData() allocated only 0xA8 (168) bytes + and initialised cbStruct to 168. +• Starting with newer OS builds the provider needs 0xD0 (208) bytes; + WinVerifyTrust therefore writes 208 bytes regardless of the smaller + allocation. + +All higher-level helpers (WldpIsDetachedSignatureTrusted, +WldpIsEmbedSignatureTrusted, WldpIsCatalogSignatureTrusted and the +vector wrapper WldpIsFileWithDetachedSignatureTrusted) passed this +undersized buffer directly to WinVerifyTrust. When the latter wrote +the extra 0x28 bytes the memory directly following the allocation was +clobbered: + +1. Stack locals such as v29/v30 in WldpIsDetachedSignatureTrusted were + over-written. +2. Corrupted locals were later interpreted as policy flags and return + codes, making untrusted files appear trusted. +3. Because the overflow happens in kernel address space (wldp.dll is + loaded into every protected process) it may also lead to memory + corruption and potential code execution. + +In effect, the signature result is accepted without the mandatory +fields ever being verified – a direct security-feature bypass. + +Structures/parameters involved +• CONFIG_CI_PROV_INFO_RESULT2 (old size 0xA8, new size 0xD0) +• cbStruct (offset 0) +• WinVerifyTrust(WTD_GENERIC_CERT_POLICY) +• Call stacks beginning at WldpIs*SignatureTrusted helpers. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old – allocates too little (0xA8) and sets size to 168 +_QWORD *__fastcall NewProviderData(_QWORD *a1) { + _DWORD *p = LocalAlloc(0x40u, 0xA8ui64); + if (p) { *p = 168; *a1 = p; } +} + +// new – allocates correct size (0xD0) and sets size to 208 +_QWORD *__fastcall NewLegacyProviderData(_QWORD *a1) { + _DWORD *p = LocalAlloc(0x40u, 0xD0ui64); + if (p) { *p = 208; *a1 = p; } +} + +// old call-site (excerpt) +WinVerifyTrust(HWND_MESSAGE|0x2, &stru_1800420A8, &pWVTData); +// pWVTData points to 0xA8-byte buffer. + +// new call-site (excerpt) +_OWORD v40[13]; // 13*16 = 0xD0 bytes +LODWORD(v40[0]) = 208; +WinVerifyTrust(HWND_MESSAGE|0x2, &stru_18004B658, &pWVTData); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User supplies a file with a detached/embedded/catalog signature. +2. WDAC calls WldpIsFileWithDetachedSignatureTrusted(). +3. That invokes WldpIsDetachedSignatureTrusted() or its variants. +4. NewProviderData() allocates 0xA8 bytes and passes it to + WinVerifyTrust(). +5. WinVerifyTrust writes 0xD0 bytes – overflow. +6. Overwritten locals make the helper believe the signature is valid; + caller receives STATUS_SUCCESS even for an invalid or malicious + signature. + +Attack Vector +-------------------------------------------------------------------- +A local attacker places a file (script, PE, catalog) with a crafted +signature on the system and causes any WDAC-protected component to +perform a signature check. No elevated privileges are needed because +the overflow occurs inside the signature-checking routine itself. + +Patch Description +-------------------------------------------------------------------- +1. Introduced NewLegacyProviderData() that allocates 0xD0 bytes and + sets cbStruct to 208. +2. All WldpIs*SignatureTrusted helpers were rewritten to + a. use an on-stack 0xD0-byte buffer when interacting with + WinVerifyTrust; + b. treat the result parameter as _OWORD[13] (208-byte) instead of a + raw pointer; + c. call WTConfigCiFreePrivateData() to zero/free the buffer. +3. GUIDs updated (stru_18004B658) to reference the correct provider + action. +4. Added extensive memset() calls to wipe residual data. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a malicious file could pass WDAC signature checks, +allowing execution of code that should have been blocked (Security +Feature Bypass). Because the bug is an out-of-bounds write in kernel +address space, it also opened the door for reliability issues and +potential privilege escalation via memory corruption. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code allocates/initialises the full 0xD0-byte structure and +never hands an undersized buffer to WinVerifyTrust, eliminating the +overflow and restoring correct signature verification. All call +sites were updated, and provider data is now zeroed after use, +mitigating information-leak vectors. No residual paths using the old +0xA8 allocation were observed, so the fix appears complete. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33070_netlogon.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33070_netlogon.dll.txt new file mode 100644 index 0000000..38f6b18 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33070_netlogon.dll.txt @@ -0,0 +1,113 @@ +{'kb': 'KB5060842', 'date': 1763415594.61646, 'change_count': 46, 'confidence': 0.33, 'patch_store_uid': '77729ec0-6d71-4d12-ac3e-650525e5c1b2', 'file': 'netlogon.dll', 'cve': 'CVE-2025-33070'} +-------------------------------------------------------------------- +CVE-2025-33070 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – Netlogon service (netlogon.dll) + +Vulnerability Class +-------------------------------------------------------------------- +Use of Un-initialised Resource / unchecked-return-value leading to +privilege-elevation (CWE-908) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Netlogon calculates authentication credentials through the internal + helper NlComputeCredentials(). The routine can fail at several + points (BCryptGetProperty, BCryptGenerateSymmetricKey, BCryptEncrypt + or SystemFunction001) and returns the NTSTATUS error code. + +2. Prior to the patch the callers (e.g. NlBuildAuthenticator(), + NlUpdateSeed(), NlCheckAuthenticator(), NlpUserValidateHigher(), + etc.) completely ignored this return value. They continued to use + the output buffers (pbOutput / Authenticator / Seed) even when the + function had aborted and the data were never initialised. Those + buffers therefore contained predictable all-zero data. + +3. The unchecked path was taken for both the classic RC4 as well as + the modern AES credential path (flag 0x0100 0000). An attacker who + can force any of the failure conditions (for example by disturbing + the CNG provider so that BCryptGetProperty(…ObjectLength…) fails) + receives a session where: + • the server’s seed is incremented, + • the server builds its authenticator from zeroed credential data, + • AccessCheck on the resulting RPC call succeeds because the + authenticator comparison also uses the same zero buffer. + +4. Because Netlogon runs in LSASS, successful authenticator acceptance + yields an authenticated (and signed/sealed) channel. This provides + the remote, unauthenticated attacker with machine-account level + privileges (EoP over the network). + +5. The same unchecked pattern existed in the reverse direction + (NlUpdateSeed / server->client). + +6. Additional damage: NetpAccessCheck2() accepted an uninitialised + GENERIC_MAPPING parameter provided by the caller. When that caller + supplied garbage, AccessCheck() operated on undefined masks and + could grant full access. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch – NlComputeCredentials return value ignored +NlComputeCredentials(pbInput, pbOutput, pbIV, a4); // status lost +... +return NlpDumpBuffer(...); + +// post-patch – callers now verify status +status = NlComputeCredentials(...); +if (status < 0) { + NlPrintRoutine(...); + return status; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends crafted RPC to any Netlogon endpoint. +2. NetrLogon* entry point → NlBuildAuthenticator() / NlUpdateSeed(). +3. NlComputeCredentials() fails – returns NTSTATUS ≠ 0 +4. Caller (pre-patch) ignores failure → uses zeroed buffers. +5. Server compares zeroed authenticator with attacker supplied one – + they match → authentication accepted. +6. LSASS grants attacker machine-account privileges. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network access to the Netlogon RPC interface over the +Domain-joined network (TCP / SMB named-pipe or DCERPC). + +Patch Description +-------------------------------------------------------------------- +• Converted NlComputeCredentials() and companion helpers to return a + 64-bit NTSTATUS and changed calling convention so that every caller + must check the result. +• Added comprehensive error handling and early-exit paths in + NlBuildAuthenticator(), NlUpdateSeed(), NlCheckAuthenticator(), + NlpUserValidateHigher(), NlDnsSendRemoteUpdate(), etc. +• Filled output buffers with zeros only after successful crypto + operations. +• Removed externally supplied GENERIC_MAPPING from NetpAccessCheck2() + and NetpCreateSecurityObject(); the functions now use the fully + initialised internal mapping (NlGlobalNetlogonInfoMapping). +• Hardened NetpAccessCheck2() signature and all its call-sites. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an unauthenticated attacker could: +• Obtain a signed Netlogon secure channel using an all-zero + authenticator, +• Execute privileged Netlogon RPCs, +• Ultimately elevate privileges to Domain Admin / SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +Returning NTSTATUS and enforcing rigorous propagation of failure codes +eliminates the use of un-initialised credential buffers. The +additional sanity checks in NetpAccessCheck2() prevent misuse of an +uninitialised GENERIC_MAPPING. No path remains where authentication +continues after a cryptographic failure; therefore the vulnerability is +considered fully remediated. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33073_mrxsmb.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33073_mrxsmb.sys.txt new file mode 100644 index 0000000..687e44b --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33073_mrxsmb.sys.txt @@ -0,0 +1,141 @@ +{'change_count': 5, 'file': 'mrxsmb.sys', 'patch_store_uid': '238e095d-a913-4a7d-b7e3-94a2acac5569', 'confidence': 0.18, 'date': 1763417337.1348724, 'kb': 'KB5060842', 'cve': 'CVE-2025-33073'} +-------------------------------------------------------------------- +CVE-2025-33073 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows SMB client kernel driver (mrxsmb.sys). The faulty +code resides in the helper routines that prepare SMB packets for +transport: + • RxCeEncryptData() + • RxCeCompressData() + • SmbCeCreateSrvCall() +These functions run in kernel-mode and manipulate attacker-influenced +lengths while building encrypted / compressed messages and while +creating a server connection. + + +Vulnerability Class +-------------------------------------------------------------------- +Integer overflow / buffer under-allocation that leads to out-of-bounds +write (memory corruption) and therefore to local elevation of +privilege. Classified under CWE-284 (improper access control) but the +technical core is an unchecked arithmetic operation followed by kernel +pool overwrite. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. RxCeEncryptData + • Caller supplies parameter a4 = PlainTextLength. + • Old code blindly computed TotalAlloc = a4 + 132 and allocated that + much non-paged pool (ExAllocatePool2 tag ‘SmBf’). + • It then set outBuffer = Pool + 80, wrote a 52-byte header, and + copied/encrypted a4 bytes into outBuffer+52. + • If a4 is very large (>= 0xFFFFFFED) the 64-bit addition wraps and + TotalAlloc becomes smaller than 80+52+a4, leaving the copy and the + encrypt routine writing past the end of pool memory. + • If a4 is very small the function can return *a6 = 52 (minimum + header) while IoAllocateMdl() is later called with a size that is + smaller than required, again leading to pool corruption when the + buffer is used by the network stack. + +2. RxCeCompressData + • Similar pattern: required size was calculated as + Needed = a4 + (a5 ? 96 : 16) + without overflow checking. + • Small or overflowed ‘a4’ values made Required < HeaderLength, + causing a later memmove()/SmbCompression*() to overrun the + allocation. + +3. SmbCeCreateSrvCall + • The server-call constructor eventually feeds user-controlled + lengths to the two helpers above. Patch adds early rejection via + CredUnmarshalTargetInfo() and SmbCeQueryServerAvailability() as + well as the same feature-flagged size sanity checks. + +Corrupted pool memory originates from attacker-controlled network +traffic, ultimately allowing code execution in the kernel context of +SYSTEM and giving full local privilege escalation. + +Structures / fields affected + RXCE_ENCRYPT_HEADER (52 bytes) – written past allocation. + SMB_COMPRESSION_HDR (variable) – same. + MDL.Size – MDL created for undersized backing buffer. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old RxCeEncryptData +*a6 = a4 + 52; // length returned to caller +Pool = ExAllocatePool2(NonPaged, a4 + 132, 'SmBf'); // no checks +out = Pool + 80; // start of header +RtlCopyMdlToBuffer(..., Pool + 132, a4, ...); // may overrun +``` +```c +// Patch +v9 = a4 + 52; +if (FeatureFlag && v9 < 0x34) { *a6 = -1; return STATUS_INVALID; } +... +v10 = v9 + 80; +if (v10 < 0x50) return STATUS_INVALID_PARAMETER; +Pool = ExAllocatePool2(NonPaged, v10, 'SmBf'); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Malicious SMB server sends crafted packet inducing the client to + call SmbCeCreateSrvCall(), leading to RxCeEncryptData()/ + RxCeCompressData() with attacker-controlled length (a4). +2. Length chosen so that (a4 + constant) wraps below the required + space (e.g., 0xFFFFFFF0). +3. Kernel allocates undersized pool. +4. Subsequent copy/encrypt/compress writes beyond buffer boundary. +5. Pool metadata or adjacent objects are corrupted; attacker sprays + data and gains arbitrary code execution, elevating to SYSTEM. + + +Attack Vector +-------------------------------------------------------------------- +Remote, unauthenticated SMB server or man-in-the-middle that returns +crafted length fields in SMB2 TRANSFORM or COMPRESSION responses. No +local privileges are required on the client machine; only outbound +SMB connection is necessary. + + +Patch Description +-------------------------------------------------------------------- +1. Added explicit integer-overflow / size-minimum checks before every + arithmetic that computes a pool allocation size. +2. Rejects any request when (size + header) wraps or is below the + smallest legal frame (0x34 / 0x50 bytes). +3. Returns STATUS_INVALID_PARAMETER (0xC000000D) instead of proceeding. +4. Introduced helper Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_*() + as a gate so the extra validation can be toggled but is on by + default. +5. In SmbCeCreateSrvCall additional credential parsing and + SmbCeQueryServerAvailability() verification stops the path earlier + if malformed input is detected. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a remote attacker could corrupt kernel non-paged pool +from a low-privilege client context, leading to arbitrary code +execution in kernel mode. Successful exploitation yields full system +privileges (EoP) and potentially allows sandbox escape or security +boundary bypass over the network. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added arithmetic checks guarantee that every buffer allocated is at +least as large as the subsequent header + payload writes and that +integer wrap-around is impossible on 32/64-bit builds. All dangerous +paths are now gated by the same validation logic; no write occurs +unless the allocation succeeds with a verified size. Therefore the +original out-of-bounds condition is removed. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33073_mrxsmb20.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33073_mrxsmb20.sys.txt new file mode 100644 index 0000000..8e8bec2 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33073_mrxsmb20.sys.txt @@ -0,0 +1,145 @@ +{'kb': 'KB5060842', 'patch_store_uid': '17962b99-8d67-4355-a10e-0dc627626a42', 'confidence': 0.17, 'date': 1763417318.511233, 'file': 'mrxsmb20.sys', 'cve': 'CVE-2025-33073', 'change_count': 1} +-------------------------------------------------------------------- +CVE-2025-33073 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows SMB v2/v3 client driver (mrxsmb20.sys) – routine +Smb2Fsctl_Finalize that completes SMB2 IOCTL/FSCTL requests. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Missing Output-buffer Validation (CWE-284). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Smb2Fsctl_Finalize is invoked when an SMB2 IOCTL (FSCTL) operation +completes. The RxContext for the request is referenced through + + v4 = *(QWORD *)(a1 + 48); // RX_CONTEXT + *(DWORD *)(v4 + 524) // IoControlCode + *(QWORD *)(v4 + 184) // Output-buffer length + *(QWORD *)(a1 + 1992) // Mdl / system address of buffer + +For the FSCTL whose code equals 0x90444 (decimal 590468) the routine +used to return to caller without validating that the server-supplied +FILE_REGION output structure actually fits into the user-supplied +buffer. + +If a malicious SMB server set an artificially large Length field in the +network response, *(DWORD *)(v4 + 184) would contain the oversized +value. The driver would later copy that many bytes into the caller’s +buffer (or into kernel bookkeeping structures) leading to: + + • Out-of-bounds read/write in kernel address space + • Potential kernel stack/heap corruption + • Privilege escalation for the local caller that issued the FSCTL. + +The patch introduces a size/veracity check by calling: + + FsRtlValidateFileRegionOutputBuffer( + v4 + 184, // reported size + *(DWORD *)(a1 + 2000), // element size / alignment + *(QWORD *)(a1 + 1992), // buffer base + v2); // NTSTATUS + +The validation is only executed when + + * IoControlCode == 0x90444, and + * the operation completed with an error (NT_ERROR(status) || + status == STATUS_BUFFER_OVERFLOW), and + * the Feature_H2E_WPA3SAE feature flag is enabled (gating for + down-level OS builds). + +Prior to the patch no analogous check existed, leaving the kernel state +fully trusting the remote server’s length field. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if (*(DWORD *)(v4 + 524) == 589992 && + ((int)(v2 + 0x80000000) < 0 || v2 == -2147483643)) +{ + v2 = FsRtlValidateReparsePointBufferEx(...); +} +// no handling for IoControlCode 590468 (0x90444) + +// AFTER +if (*(DWORD *)(v4 + 524) == 589992 && + (((v2 + 0x80000000) & 0x80000000) != 0 || + v2 == -2147483643)) +{ + v2 = FsRtlValidateReparsePointBufferEx(...); +} + +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_0() && + *(DWORD *)(v4 + 524) == 590468 && + (((v2 + 0x80000000) & 0x80000000) != 0 || + v2 == -2147483643)) +{ + v2 = FsRtlValidateFileRegionOutputBuffer( + v4 + 184, + *(DWORD *)(a1 + 2000), + *(QWORD *)(a1 + 1992), + v2); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local low-privilege process issues DeviceIoControl to the SMB RsFx + redirector targeting a remote share with FSCTL code 0x90444. +2. Malicious SMB server replies with crafted FILE_REGION response where + Length > real buffer size. +3. Smb2Fsctl_Finalize trusts *(DWORD *)(v4 + 184) and later copies that + many bytes to user buffer or internal kernel memory. +4. Memory corruption or privilege escalation occurs in kernel context. + + +Attack Vector +-------------------------------------------------------------------- +Authenticated network attacker controlling an SMB server that the +victim machine connects to. The local victim process only needs the +ability to open the share – no special privileges required. The server +sends an oversized FILE_REGION reply to elevate the local user or +execute code in kernel mode on the client machine. + + +Patch Description +-------------------------------------------------------------------- +1. Added explicit size validation for FSCTL 0x90444 via + FsRtlValidateFileRegionOutputBuffer. +2. Corrected logic that determines whether the earlier operation status + is success or failure (reversed sign checks). +3. Minor refactoring/renaming; no functional impact beyond validation. + +The new validation aborts the request with STATUS_INVALID_PARAMETER if +any of the following is true: + • Reported length < required header size + • Reported length not aligned + • Reported length would overflow the caller’s buffer. + + +Security Impact +-------------------------------------------------------------------- +Before the fix, remote servers could coerce the Windows SMB client into +kernel memory corruption, allowing local elevation of privilege or +potential remote code execution in kernel context. Exploitation does +not require administrator rights on the client. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added FsRtlValidateFileRegionOutputBuffer routine centrally +verifies buffer boundaries, fully preventing the length mismatch that +enabled the overwrite. The validation is performed before any data is +consumed or copied, and negative status is propagated back to user +mode, eliminating the vulnerability. + +-------------------------------------------------------------------- diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33075_msi.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33075_msi.dll.txt new file mode 100644 index 0000000..189e6a2 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-33075_msi.dll.txt @@ -0,0 +1,173 @@ +{'file': 'msi.dll', 'confidence': 0.31, 'change_count': 13, 'date': 1763415617.7336595, 'kb': 'KB5060842', 'patch_store_uid': 'a368fe0d-9e6c-4521-9671-fa9b783254fa', 'cve': 'CVE-2025-33075'} +-------------------------------------------------------------------- +CVE-2025-33075 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Installer (msi.dll) – file-handling routines executed under +SYSTEM during install / uninstall: + • CMsiOpExecute::ixfFileRemove + • CMsiOpExecute::CreateFileFromData + • CMsiOpExecute::ReplaceFileOnReboot + • support paths in CMsiExecute::RunInstallScript and SAFER + verification helpers + + +Vulnerability Class +-------------------------------------------------------------------- +Improper link resolution before file access (CWE-59, symlink / hard- +link following) leading to local privilege escalation. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the installer executed privileged file operations +(DeleteFile, MoveFileExW + MOVEFILE_DELAY_UNTIL_REBOOT, CreateFileW +with GENERIC_WRITE) on pathname arguments supplied by the MSI +package without first resolving and validating the final object. + +1. CMsiOpExecute::ixfFileRemove + • Receives an IMsiRecord describing the file to delete. + • Calls CMsiOpExecute::RemoveFile() or schedules the deletion when + the record is processed. + • The pre-patch code never checked whether the pathname traversed a + reparse-point; any junction / mount-point planted by a low- + privileged attacker would therefore be followed and the target + file removed with SYSTEM rights. + +2. CMsiOpExecute::CreateFileFromData + • Creates or overwrites a file by simply invoking + CreateFileW(path, 0x40000000, …). + • No validation of the destination; symbolic-link or hard-link + redirection allowed the write to escape the intended directory. + +3. CMsiOpExecute::ReplaceFileOnReboot + • Schedules a MoveFileExW(src, dst, MOVEFILE_DELAY_UNTIL_REBOOT). + • Again, the original code used the raw MSI-supplied names. + +Because MSI actions run inside the Windows Installer service, +attackers that can start an installation (standard users are usually +allowed) could point the path fields at links that resolve to +protected locations (e.g. \Windows\System32\drivers\etc\hosts). At +install time or after reboot the service would unknowingly delete or +overwrite those files, achieving arbitrary file overwrite and thus +privilege escalation to SYSTEM. + +Patch-level changes +─────────────────── +The updated code introduces a new "AutoIdentityGenerationHook" feature +and an AppIdHelper object that mediates every privileged file +operation. + +• RunInstallScript now initialises AppIdHelper and sets an + "operation-in-progress" byte (offset 0x88/0x136) before calling + ixfFileRemove; the byte is cleared afterwards to guarantee the + helper is active only for privileged phases. + +• ixfFileRemove + – If the feature is enabled and the helper’s flag is set, the code + no longer reaches RemoveFile(); instead it resolves the final + object path (Path::ResolveToRoot + GetFinalPathNameByHandle), then + hands it to AppIdHelper::CollectFileInfo(). + – When the flag is not set the normal delete path is taken, but only + after validation. + +• CreateFileFromData / ReplaceFileOnReboot + – New call sequence: + CheckSystemDrivePath(path,&isSysDrv) + IsAdmin(), GetAISToken(), AISFlag + if ( violation ) → abort + – Only if the path is on the system drive and the caller is suitably + privileged does the code proceed to CreateFileW / MoveFileExW. + +• If validation fails the function drops into a diagnostic path and + returns ERROR_ACCESS_DENIED, leaving the privileged file untouched. + +These checks ensure that the resolved final object lies inside a safe +location, that no reparse-point redirection occurs, and that the user +context holds the correct privilege set before the sensitive write or +rename is executed. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ReplaceFileOnReboot – before +if ( MoveFileExW(a2, a3, 4u) ) { … } + +// ReplaceFileOnReboot – after +if ( CheckSystemDrivePath(a2,&drvOk)!=1 || + (!IsAdmin() && !GetAISToken() && drvOk!=1) ) + goto abort; // path rejected +… +if ( MoveFileExW(a2, a3, 4u) ) { … } +``` +```c +// ixfFileRemove – before +v8 = CMsiOpExecute::RemoveFile(a1, v31, v30, …); + +// ixfFileRemove – after (feature enabled) +AppIdHelper::CollectFileInfo(helper, resolvedPath); // no delete +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Standard user crafts an MSI package whose FileRemove or + FileInstall table entry points to + C:\Users\Public\Logs\link_to_System32\drivers\etc\hosts. +2. Before running msiexec, attacker creates the reparse-point (junction + or hard-link) so the path resolves to an arbitrary protected file. +3. User launches msiexec /i evil.msi – this spawns the Windows + Installer service running as SYSTEM. +4. Pre-patch service follows the link and deletes / overwrites the + target file, leading to SYSTEM-level file write. +5. Post-patch service rejects the path during CheckSystemDrivePath or + bypasses the delete when AppIdHelper is active, aborting the attack. + + +Attack Vector +-------------------------------------------------------------------- +Local; attacker only needs the ability to start an MSI installation +(which is normally permitted for standard users) and create a +filesystem link in a writable directory. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced feature gate + wil::Feature<__WilFeatureTraits_Feature_AutoIdentityGenerationHook>. +2. New helper (AppIdHelper) that: + • stores current user SID, package name, operation type; + • collects canonicalised file paths and security data; + • suppresses privileged delete when gathering data. +3. Added strong path validation: + • CheckSystemDrivePath(), IsAdmin(), GetAISToken() + • Rejects paths not on the system drive or lacking privilege. +4. Guarded all MoveFileExW / CreateFileW / RemoveFile calls with the + above checks. +5. Added explicit reparse-point safe-open logic (GetFinalPathName & + FILE_FLAG_OPEN_REPARSE_POINT) inside helper (not shown in diff but + implied by CollectFileInfo()). + + +Security Impact +-------------------------------------------------------------------- +Before the fix a local, low-privileged attacker could coerce the +Windows Installer service into deleting or overwriting arbitrary files +owned by SYSTEM, resulting in elevation of privilege. After the fix +the service refuses to operate on paths that escape the trusted +context, preventing the link-following attack. + + +Fix Effectiveness +-------------------------------------------------------------------- +• Paths are canonicalised and validated before any privileged file + create, delete or rename. +• Operations are silently aborted when validation fails, closing the + exploit primitive. +• Additional telemetry (AppIdHelper) makes it easier to audit any + future failures. +No bypass is evident in the patched code; the vulnerable call sites +are now protected by the new checks, making the patch effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47160_shell32.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47160_shell32.dll.txt new file mode 100644 index 0000000..591867e --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47160_shell32.dll.txt @@ -0,0 +1,130 @@ +{'date': 1763415683.5856595, 'kb': 'KB5060842', 'cve': 'CVE-2025-47160', 'file': 'shell32.dll', 'confidence': 0.2, 'change_count': 621, 'patch_store_uid': '64ea4d34-cd00-491c-9cb4-69ae2c642f68'} +-------------------------------------------------------------------- +CVE-2025-47160 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Shell (shell32.dll) shortcut (.LNK) handling, new helper +function IsLinkTargetUnusual() introduced in the patch. + +Vulnerability Class +-------------------------------------------------------------------- +Protection-mechanism failure / security feature bypass (CWE-693). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch the Shell validation logic had no dedicated test to +classify a shortcut target as "unusual". Down-stream code therefore +assumed that the target string returned by IShellLinkW::GetPath fitted +into the classic MAX_PATH sized buffer (260 WCHARs) and contained no +problematic characters. Two separate edge cases were not covered: + +1. Long target paths – when the real link target exceeded 260 + characters IShellLinkW::GetPath replied with + HRESULT = 0x8007007A (ERROR_INSUFFICIENT_BUFFER) but the caller did + not treat this as a failure. The truncated path was consequently + handled as if it had been completely validated. + +2. Embedded command-line arguments – a shortcut can contain additional + arguments stored in the PKEY_Link_Arguments property. These + arguments were not inspected for shell-meta characters. Crafting a + string such as "powershell.exe -Command … &" allowed an attacker to + smuggle special characters that should have triggered a warning. + +Because of these omissions the overall "is this link suspicious?" +heuristic could be bypassed. A remote attacker able to supply a LNK +file (for example over SMB/WebDAV or in a ZIP) could construct a path +longer than 260 characters and/or inject special characters into the +argument property. The shell would fail to classify the file as +unusual and would therefore skip subsequent security checks or user +prompts that rely on that classification. + +Structures/parameters involved: +• IShellLinkW vtable – GetPath, QueryInterface +• PROPERTYKEY PKEY_Link_Arguments +• 260-WCHAR stack buffer supplied to GetPath +• Link flags returned by SetLinkFlags(); bits 0x20 and 0x1000 were + consulted by the new routine. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// NEW in patch – shortened for clarity +bool __fastcall IsLinkTargetUnusual(IShellLinkW *psl) +{ + WCHAR sz[264] = {0}; + WORD flags = SetLinkFlags(psl, 0, 0); + if (flags & 0x1000) // early exit for some benign flags + return false; + + HRESULT hr = psl->lpVtbl->GetPath(psl, sz, 260, 0, 0); + if (hr == 0x8007007A) // ERROR_INSUFFICIENT_BUFFER + return true; // path did not fit – unusual + + if (SUCCEEDED(hr)) { + PathQuoteSpacesW(sz); + size_t len = wcslen(sz); + if (flags & 0x20) { // link has extra arguments + /* fetch argument string and scan for specials */ + if (HasSpecialChars(argString)) + return true; + len += wcslen(argString) + 1; + } + return len >= 0x104; // >260 characters → unusual + } + return false; +} +``` +Prior to the patch the above routine did not exist; the caller simply +proceeded after GetPath(), ignoring both the 0x8007007A error and the +argument string contents. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens or previews a crafted .LNK file obtained over the network. +2. Shell32 calls IShellLinkW::GetPath with a 260-char buffer. +3. Long path causes ERROR_INSUFFICIENT_BUFFER or is truncated. +4. Because the pre-patch code never classified the link as unusual, + downstream security prompts (SmartScreen, Zone checks, etc.) are + skipped. +5. Shortcut executes the attacker-controlled target / arguments without + usual user consent. + +Attack Vector +-------------------------------------------------------------------- +Malicious shortcut file delivered over SMB, WebDAV, email attachment or +inside an archive. Victim only needs to browse to or open the file; +no administrative privileges required. + +Patch Description +-------------------------------------------------------------------- +The update adds a new helper function IsLinkTargetUnusual() that is +called from the decision path that previously lacked validation. The +routine: +• Detects ERROR_INSUFFICIENT_BUFFER from GetPath. +• Calculates the total length of target + arguments and flags anything + >=0x104 (260) characters. +• Extracts PKEY_Link_Arguments and invokes HasSpecialChars() to search + for shell-meta characters. +• Treats any of the above conditions as "unusual", causing the caller + to fall back to a safer execution path that invokes additional + security checks or user warnings. + +Security Impact +-------------------------------------------------------------------- +Without the patch an attacker could reliably bypass the Windows Shell +"unusual link" heuristic, causing shortcuts with excessively long paths +or dangerous command-line arguments to execute without the standard +security prompt. This yields a security feature bypass that may be +combined with other vulnerabilities to achieve remote code execution or +privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code explicitly handles buffer size errors, validates total +length, and scans arguments for special characters. These checks cover +the previously missed cases and force the caller to treat such links as +unusual. Assuming all call sites now invoke the helper, the patch +fully addresses the identified bypass. No residual paths are visible +in the provided diff. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47160_shlwapi.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47160_shlwapi.dll.txt new file mode 100644 index 0000000..ff8a891 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47160_shlwapi.dll.txt @@ -0,0 +1,152 @@ +{'file': 'shlwapi.dll', 'cve': 'CVE-2025-47160', 'change_count': 9, 'kb': 'KB5060842', 'confidence': 0.23, 'date': 1763415717.84932, 'patch_store_uid': '0f118e1c-8507-4ccb-ad1d-08fd74d96fee'} +-------------------------------------------------------------------- +CVE-2025-47160 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Shell – shlwapi.dll, routine SHMessageBoxCheckExW + + +Vulnerability Class +-------------------------------------------------------------------- +Security-feature bypass / Protection-mechanism failure (CWE-693) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SHMessageBoxCheckExW is the helper that shows the security dialog +("Open File – Security Warning", "Are you sure you want to run this +file?", etc.) when the shell opens content that is considered risky – +for example a *.LNK* file that originates from a remote share or the +Internet zone. The routine receives + + (hInstance, lpTemplateName, hWndParent, a4, a5, a6, pszValue) + +and calls DialogBoxParamW to create the dialog that enforces the user +confirmation. + +Prior to the patch the function: + 1. Obtained the dialog template from the module resources + (lpTemplateName). + 2. Directly called DialogBoxParamW with that template. + +DialogBoxParamW automatically scales only the font, not the dialog +layout. When the user has enabled *Text Scaling* ( Ease-of-Access +-> “Make text larger” ) or high DPI settings, the static template is +automatically enlarged while the controls keep their original layout. +As a result several critical controls – notably the checkbox that +stores the registry value “Always ask before opening this file” and +the *Cancel/Don’t run* button – are rendered outside of the client +area and therefore become invisible and unreachable. + +Because the message box is modal and the default button is the +"Run/OK" action, a user under such scaling conditions is presented +with a dialog that contains only a single visible option that +confirms execution. The protection dialog is still technically +shown, but the user has no practical chance to abort. An attacker +who distributes a malicious shortcut file can therefore rely on text +scaling to defeat the warning and launch arbitrary content without +any effective barrier. + +The condition is completely under control of the attacker because the +text-scaling factor can be forced remotely (through a packaged +accessibility theme, by embedding a manifest that sets +preferDpiAwareness, or by instructing the victim how to enable the +feature). No administrator privileges are required. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable path (before) +ulCookie = 0; +v16 = v11; // build init-struct for dlg proc +v17 = pszValue; +v18 = a4; +v19 = a5; +SHActivateContext(&ulCookie); +// static template – no scaling performed +v13 = DialogBoxParamW(hInstance, lpTemplateName, hWndParent, + MessageBoxCheckExDlgProc, + (LPARAM)&dwInitParam); +SHDeactivateContext(ulCookie); +``` + +```c +// fixed path (after) +if (Feature_TextScaleDialogTemplate_IsEnabled()) { + hDialogTemplate = NULL; + if (TextScaleDialogTemplate(hInstance, lpTemplateName, + &hDialogTemplate) >= 0) + { + // layout is rebuilt for the current scaling factor + v13 = DialogBoxIndirectParamW(hInstance, hDialogTemplate, + hWndParent, + MessageBoxCheckExDlgProc, + (LPARAM)&dwInitParam); + } else { + v13 = DialogBoxParamW(...); // fallback + } + LocalFree(hDialogTemplate); +} else { + v13 = DialogBoxParamW(...); // legacy behaviour +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Victim opens a crafted *.LNK* file that points to remote code. +2. Shell32 performs a zone check and calls SHMessageBoxCheckExW to + display the security prompt. +3. Victim system has text-scaling >100 % (or the attacker forces it). +4. DialogBoxParamW creates the dialog with a mismatching layout; the + Cancel button and warning checkbox are clipped. +5. Only the default *Run* button is visible; user can not refuse. +6. Malicious payload executes without effective user confirmation. + + +Attack Vector +-------------------------------------------------------------------- +Remote (network). An attacker shares or emails a malicious shortcut +file and convinces the target to open it while text scaling is +active. No further privileges are needed. + + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the static-template creation path with a feature- +gated routine that: + 1. Dynamically rebuilds (TextScaleDialogTemplate) the dialog + DLGTEMPLATEW structure according to the current text-scaling/DPI + factor. + 2. Feeds that scaled template to DialogBoxIndirectParamW so that all + controls are repositioned correctly and remain visible. + 3. Frees the temporary template with LocalFree. +If the scaling routine fails or the feature flag is off, the code +falls back to the original behaviour. The change is entirely inside +SHMessageBoxCheckExW; no other callers have to be modified. + + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could reliably suppress the actionable +parts of the security prompt and therefore bypass the user-consent +requirement for running content from untrusted locations. This +removes a major defence in depth measure in Windows Explorer and +other Shell-based launch paths. Successful exploitation gives the +attacker the same execution context as the victim user. + + +Fix Effectiveness +-------------------------------------------------------------------- +The fix addresses the root cause by guaranteeing that all controls +are laid out for the effective text-scaling factor, irrespective of +DPI settings. Because the template is rebuilt at run time, no amount +of client-side scaling can hide buttons or checkboxes anymore. +Fallback to the legacy path only occurs on failure of the scaling +helper, in which case the behaviour reverts to pre-patch. Provided +TextScaleDialogTemplate cannot fail for common scaling values, the +patch fully restores the intended protection dialog. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47955_rasmans.dll.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47955_rasmans.dll.txt new file mode 100644 index 0000000..a50135c --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47955_rasmans.dll.txt @@ -0,0 +1,122 @@ +{'change_count': 2, 'confidence': 0.34, 'patch_store_uid': 'c5dda83d-c37f-4c8a-9af4-4fc30307b972', 'file': 'rasmans.dll', 'kb': 'KB5058411', 'date': 1763415427.2747135, 'cve': 'CVE-2025-47955'} +-------------------------------------------------------------------- +CVE-2025-47955 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +rasmans.dll – function SetCachedCredentials (Windows Remote Access +Connection Manager service) + +Vulnerability Class +-------------------------------------------------------------------- +Stack-based buffer overflow leading to local privilege escalation +(CWE-121) – originally reported as Improper Privilege Management +(CWE-269) because the overflow occurs in a SYSTEM service callable by +normal users. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SetCachedCredentials converts three caller-supplied ANSI strings +(username, domain and password) into Unicode using +RtlAnsiStringToUnicodeString(). Prior to the patch the destination +buffers were defined as single-byte stack variables: + + char v21; // username + char v22; // domain + char v23; // password + +Immediately before the conversion the code sets +UNICODE_STRING.MaximumLength to twice the caller-supplied length plus +the UTF-16 terminator, but still points Buffer to the 1-byte local +variable: + + v16.MaximumLength = 2 * (inputLen + 1); + v16.Buffer = (PWSTR)&v21; // size = 1 byte ! + RtlAnsiStringToUnicodeString(&v16,&AnsiName,FALSE); + +Because MaximumLength is far larger than the actual allocation, +RtlAnsiStringToUnicodeString blindly writes the converted string and +terminator well past the end of the stack variable, corrupting the +stack frame (saved RIP, frame pointer, locals, SEH chain, etc.). The +same pattern is repeated for v22 and v23. + +The function executes inside the Remote Access Connection Manager +(rasman) service, running as LocalSystem. Any authenticated user can +reach this code path by calling RasSetCredentials or any higher-level +API that stores cached dial-up/VPN credentials. A crafted long string +(over 1 byte) therefore lets the attacker overwrite the stack and gain +code execution in the SYSTEM context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable code (before patch) +char v21; // 1 byte buffer +UNICODE_STRING v16; +... +v16.Length = 0; +v16.MaximumLength = 2 * (LOWORD(DestinationString[1]) + 1); +v16.Buffer = (PWSTR)&v21; // <-- points to 1 byte +RtlAnsiStringToUnicodeString(&v16, // overflows stack + (PCANSI_STRING)&DestinationString[1], + 0); +``` + +```c +// fixed code (after patch) +wchar_t Source[257]; // 514 bytes (inclusive NUL) +UNICODE_STRING v19; +... +v19.Length = 0; +v19.MaximumLength = 2 * (LOWORD(DestinationString[1]) + 1); +v19.Buffer = Source; // correctly sized buffer +RtlAnsiStringToUnicodeString(&v19, + (PCANSI_STRING)&DestinationString[1], + 0); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege client calls RasSetCredentials() with an over-long + user name/domain/password. +2. RPC reaches RasRpcSetCachedCredentials() inside rasman service. +3. RasRpcSetCachedCredentials invokes SetCachedCredentials(). +4. Function allocates 1-byte stack buffers v21/v22/v23. +5. RtlAnsiStringToUnicodeString copies attacker data past buffer end. +6. Stack is corrupted; attacker controls return address; SYSTEM + execution achieved. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Supply oversized credential strings (e.g. +>260 characters) through any Windows RAS credential API that ultimately +calls SetCachedCredentials. + +Patch Description +-------------------------------------------------------------------- +1. Replaced 1-byte destination buffers with appropriately sized static + arrays: + wchar_t Source[257]; + wchar_t v25[16]; + char v26; +2. UNICODE_STRING.Buffer now points to these arrays, eliminating the + overflow. +3. Added ValidateAccountDomain() call and feature-flag guard before + actually calling LsaCallAuthenticationPackage(). +4. Updated WPP tracing IDs (cosmetic). + +Security Impact +-------------------------------------------------------------------- +Before the patch any local user could execute arbitrary code in the +context of the Remote Access Connection Manager service (NT AUTHORITY\ +SYSTEM), resulting in full privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The destination buffers are now sized at least as large as the maximum +length computed for UNICODE_STRING, preventing overwriting of adjacent +stack memory. No residual overflow paths were observed in the modified +function, so the patch appears effective. Effectiveness outside of +this routine is unknown. + diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32k.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32k.sys.txt new file mode 100644 index 0000000..5d00ef7 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32k.sys.txt @@ -0,0 +1,127 @@ +{'file': 'win32k.sys', 'change_count': 9, 'date': 1763417322.5600278, 'kb': 'KB5058411', 'patch_store_uid': '3e3672c9-11f7-46a4-b039-e6038df70db6', 'confidence': 0.11, 'cve': 'CVE-2025-47969'} +-------------------------------------------------------------------- +CVE-2025-47969 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys (Windows Graphics subsystem) – WIL (Windows Internal +Library) feature-management and feature-reporting helpers + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure / KASLR bypass (CWE-200: Exposure of Sensitive +Information to an Unauthorized Actor) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several generic helper routines that live in win32k.sys and are used by +WIL feature gates were implemented with hard-coded references to a +single global descriptor symbol: + Feature_AsyncProcessFreezeThawSupport__private_descriptor + +The functions affected in the pre-patch driver include: + • wil_details_IsEnabledFallback + • wil_details_FeatureStateCache_TryEnableDeviceUsageFastPath + • wil_details_FeatureReporting_ReportUsageToService + • wil_details_FeatureReporting_ReportUsageToServiceDirect + +Although these helpers are invoked for *every* feature that win32k.sys +tracks, they always operated on – and later *exported* – the address of +the above descriptor. When user mode registers the normal + g_wil_details_pfnFeatureLoggingHook +callback, the kernel passes several parameters coming directly from the +descriptor structure. Prior to the fix the absolute kernel pointer of +Feature_AsyncProcessFreezeThawSupport__private_descriptor (and other +internal structures contained in that descriptor) was leaked to the +user callback, disclosing precise KASLR offsets and thereby violating +Virtualisation-Based Security (VBS) expectations. + +Because the same constant pointer was also used for interlocked state +updates in wil_details_FeatureStateCache_TryEnableDeviceUsageFastPath, +attackers could proactively trigger the path with arbitrary feature +IDs, deterministically leaking the address and potentially corrupting +the feature-state cache of the AsyncProcessFreezeThawSupport feature in +other sessions. + +In short, the helpers were *not parameterised*: they relied on a global +assumed to be the current feature. Any call concerning a *different* +feature therefore processed and returned the wrong descriptor, leaking +kernel-mode pointers to user space. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// wil_details_IsEnabledFallback (before) +if (a2) { + wil_details_FeatureReporting_ReportUsageToService(a1, v4, a2); + if (a2 - 3 <= 1) + wil_details_FeatureStateCache_TryEnableDeviceUsageFastPath(v4, a2); +} +... +// hard-coded global inside helper +v4 = *Feature_AsyncProcessFreezeThawSupport__private_descriptor; +``` + +```c +// wil_details_FeatureReporting_ReportUsageToService (before) +return g_wil_details_pfnFeatureLoggingHook( + 0x0359A0B0, // constant feature id + &Feature_TestValidate_logged_traits, // leaks kernel ptr + 0, + v3, + &v9, + 0, + v8, + 1); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process registers g_wil_details_pfnFeatureLoggingHook. +2. Process exercises any win32k feature that is *not* AsyncProcess... +3. win32k calls wil_details_IsEnabledFallback(). +4. The helper calls + wil_details_FeatureReporting_ReportUsageToService(). +5. The latter forwards the constant kernel address of + Feature_AsyncProcessFreezeThawSupport__private_descriptor to the user + callback – leaking the kernel pointer space. + +Attack Vector +-------------------------------------------------------------------- +Local, post-login. An unprivileged application simply needs the ability +to load a DLL that registers a WIL logging hook (a documented +mechanism). No special privileges are required beyond normal +application execution. + +Patch Description +-------------------------------------------------------------------- +1. All affected helpers were re-worked to receive an explicit *third* + parameter: a pointer to the correct per-feature descriptor. +2. Hard-coded references to + Feature_AsyncProcessFreezeThawSupport__private_descriptor were + deleted. +3. Logging helpers now extract numeric fields from the descriptor and + pass those values – not the raw pointer – to user-mode callbacks. +4. TryEnableDeviceUsageFastPath now uses the supplied descriptor for + interlocked operations, preventing cross-feature state corruption. +5. New wrapper Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_0() was + introduced; DriverEntry was modified to call it and to accommodate + the parameterised helpers. + +Security Impact +-------------------------------------------------------------------- +The pre-patch implementation discloses stable kernel addresses to any +local user. With KASLR defeated an attacker can reliably craft ROP +chains or locate kernel gadgets, significantly lowering the bar for +subsequent privilege-escalation exploits even in VBS-protected +configurations. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code eliminates the global reference, sanitises data sent to +user mode, and ensures each feature path works with its own descriptor. +No remaining code paths referencing the old global were observed in the +diff, indicating that the address-leak primitive has been closed. Full +regression testing across all feature gates is still recommended, but +based on the presented changes the fix appears complete and effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kbase.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kbase.sys.txt new file mode 100644 index 0000000..e5d1120 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kbase.sys.txt @@ -0,0 +1,117 @@ +{'confidence': 0.22, 'date': 1763417439.3351953, 'cve': 'CVE-2025-47969', 'file': 'win32kbase.sys', 'kb': 'KB5058411', 'patch_store_uid': '1556b61b-6e12-44f3-988f-2e5fbc78c3dd', 'change_count': 264} +-------------------------------------------------------------------- +CVE-2025-47969 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kbase.sys – kernel-mode Win32k privilege/permission helper +routines (IsPrivileged, HasTcbPrivilege, *Screen/Surface*AccessCheck, +SetProcessType, UIPrivilegeIsolation::CheckAccessEx, xxxInitProcessInfo +and callers). + +Vulnerability Class +-------------------------------------------------------------------- +Improper access-control / privilege–check bypass (logical flaw). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The core helper that decides whether the caller owns a required +privilege is IsPrivileged(). In the vulnerable build the routine +contained a feature-gated fast path: + + if ( Feature_IsEnabled() ) + return IsPrivilegedEx( PsGetCurrentProcess(), Required ); + +PsGetCurrentProcess() returns the KPROCESS object representing the +*process* token only; it deliberately ignores any client/thread +impersonation token, restricted token or S4U logon token which may be +active on the current thread. Consequently, if the process token +carries the privilege (e.g. SeTcbPrivilege) but the *thread* token does +not, SePrivilegeCheck should fail – yet the above shortcut returned +success. SeCaptureSubjectContext / SeLockSubjectContext were also +skipped, so auditing and mandatory policy were never evaluated. + +Whenever the internal feature flag + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_N +resolved to TRUE, every call site that ended up in IsPrivileged() – +including HasTcbPrivilege(), UserSurfaceAccessCheck(), +UserScreenAccessCheck(), SetProcessType(), xxxInitProcessInfo() and +UIPrivilegeIsolation::CheckAccessEx() – silently accepted an +unauthorised caller. + +That loophole allowed a normal local process (or even a low integrity +AppContainer if running inside the affected feature configuration) to +pass tests that were expected to be guarded by SeTcbPrivilege and +related high-integrity checks, and thus to read or influence protected +session/window state, disclosing information intended only for SYSTEM +context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// win32kbase!IsPrivileged (vulnerable) +if ( (unsigned int)Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_9() ) +{ + CurrentProcess = (struct _KPROCESS *)PsGetCurrentProcess(); + return IsPrivilegedEx(CurrentProcess, RequiredPrivileges); +} +// … normal SePrivilegeCheck path never reached when feature on. +``` + +```c +// win32kbase!IsPrivileged (patched) +memset(&SubjectContext, 0, sizeof(SubjectContext)); +SeCaptureSubjectContext(&SubjectContext); +SeLockSubjectContext(&SubjectContext); +v2 = SePrivilegeCheck(RequiredPrivileges, &SubjectContext, 1); +// no more feature-dependent shortcut +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Unprivileged user mode → win32k syscall that eventually calls +HasTcbPrivilege() → IsPrivileged() → (feature enabled) shortcut uses +process token → privilege check erroneously succeeds → protected data +returned to caller (e.g. through UserSurfaceAccessCheck → Shared surface +handles, or xxxInitProcessInfo leaking integrity/UIAccess state). + +Attack Vector +-------------------------------------------------------------------- +Local authorised user executes crafted code on the same host while the +internal *H2E_WPA3SAE* feature bit is set (device class / A/B flight +configuration). By invoking any win32k API that relies on the affected +helper, the user gains read access to otherwise restricted GUI/session +information that belongs to higher-privilege principals. + +Patch Description +-------------------------------------------------------------------- +1. Removed the entire feature-dependent fast path from IsPrivileged(). + The function now always captures and validates the full security + subject context (thread, token, impersonation) via SeCaptureSubject- + Context / SePrivilegeCheck. +2. HasTcbPrivilege() changed: when the feature is *on* it now passes the + current KPROCESS explicitly to IsPrivilegedEx(), but because the fast + path in IsPrivileged() is gone the earlier bypass no longer exists. +3. Multiple higher-level helpers (UserSurfaceAccessCheck, + UserScreenAccessCheck, UIPrivilegeIsolation::CheckAccessEx, + SetProcessType, xxxInitProcessInfo) were refactored to rely on the + corrected privilege evaluation and to drop redundant shortcuts. + Audit/trace instrumentation was also updated. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a non-privileged local attacker could pass SeTcb- +Privilege (and related) checks, obtaining GUI session details and other +protected information belonging to SYSTEM or higher-integrity +processes. The issue therefore constitutes an Information Disclosure +vulnerability (CWE-200) with a potential privilege escalation follow-up +if the leaked data is further abused. + +Fix Effectiveness +-------------------------------------------------------------------- +The shortcut that ignored thread impersonation has been completely +removed, and all callers now funnel through the standard SePrivilege- +Check machinery. No alternative path that still uses the old logic is +visible in the diff, so the patch is technically sound and should fully +mitigate the bypass. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kbase_rs.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kbase_rs.sys.txt new file mode 100644 index 0000000..c5f9f40 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kbase_rs.sys.txt @@ -0,0 +1,129 @@ +{'cve': 'CVE-2025-47969', 'confidence': 0.18, 'date': 1763417257.5230262, 'kb': 'KB5058411', 'file': 'win32kbase_rs.sys', 'patch_store_uid': '28ec5c4d-9ef9-41ef-84f9-5ba25976b62c', 'change_count': 3} +-------------------------------------------------------------------- +CVE-2025-47969 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kbase_rs.sys – Rust-based GDI region code (RegionCore) handling +scan-line uploads and flood-fill bounding-box generation. + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read / kernel information disclosure (CWE-200). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RegionCore receives region data from user-mode through two entry +points that end up in Rust functions translated to native code: + + 1. RegionCore::upload_for_floodfill(..) + 2. RegionCore::upload(..) + 3. RegionCore::insert_scanline(..) – internal helper + +The user buffer (int *a2, length a3) is expected to contain at least +one Y coordinate and one (left,right) X pair – a minimum of 3 dwords. +In the vulnerable build the following held: + + • upload_for_floodfill() rejects buffers smaller than 2 dwords but + still accepts a3 == 2 or a3 == 3 under some paths. + • When a3 < 3, unsigned arithmetic (v8 = a3-3, later used as element + count) under-flows, so insert_scanline() is invoked with an element + count that is either zero or a huge 64-bit value. + • insert_scanline() trusts this count, walks past the caller supplied + array and reads arbitrary kernel memory while trying to splice the + scan data into the region’s internal Vec<dword>. + +Even for larger, but malformed, buffers the old code did not maintain a +valid bounding box; *a1+28 (left) and +36 (right) could be left +un-initialised and later copied to user space by GDI queries, leaking +stack/heap words previously occupying those fields. + +Because the whole logic sits in win32k, the attacker only needs the +ability to call GDI region APIs from a local session – no additional +privilege is required. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// upload_for_floodfill – vulnerable cut-down version +if (a3 < 2) + panic_bounds_check(...); +// a3 == 2 still reaches here +v7 = a2[1]; +if (v7 + 1 == a2[2] && (v8 = a3-3, 2 * *a2 == v8)) + return insert_scanline(a1, v7, a2+3, v8, ...); +``` + +```c +// patched +if (FeatureFlagEnabled() && a3 < 3) + return ERROR_INVALID_PARAMETER; +... +return insert_scanline(a1, v9, (int)a2 + 12, v11, ...); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode → NtGdiCombineRgn/ExtFloodFill → + gdi_rust::region::from_path_mut() → RegionCore_set_from_path() → + RegionCore::upload_for_floodfill() → insert_scanline() + +A crafted path buffer with fewer than three integers, or with +inconsistent left/right counts, passes the old checks, causing +insert_scanline() to walk off the end of the array and disclose kernel +memory. + + +Attack Vector +-------------------------------------------------------------------- +Local, from user space. Any process that can call win32k region APIs +can feed a malformed scan buffer to win32kbase_rs.sys and obtain kernel +memory bytes via subsequent GDI queries or by examining the side +channel the read data creates. + + +Patch Description +-------------------------------------------------------------------- +1. Added feature-gated length check in upload_for_floodfill(): + • If the Win32kRSFloodFillBoundingBox feature is enabled and a3<3, + immediately return ERROR_INVALID_PARAMETER (87). +2. upload(): added an early panic for a3<2 and pervasive range checks. + Introduced logic to track min/max X while building the region; these + fields are only written once the buffer has been fully validated. +3. insert_scanline(): + • Massive restructuring; bounding-box updates are now executed only + when the feature flag indicates the new code path or when a4>0. + • All splice-in operations are preceded by explicit bounds checks; + failure now returns 14 (STATUS_NO_MEMORY) instead of touching + memory out of range. + +Overall, the patch converts several implicit unsigned wraps into +explicit comparisons and refuses dangerous buffers earlier. + + +Security Impact +-------------------------------------------------------------------- +An unprivileged local attacker could leak uninitialised kernel memory +or previous heap/stack contents from the win32kbase_rs.sys address +space. Such information can be used to defeat KASLR, leak pointers, or +further other kernel-space exploits. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new guards guarantee that: + + • No code path reaches insert_scanline() with element count smaller + than the required minimum. + • All arithmetic that could previously under-flow is replaced with + signed/size-checked logic. + • Bounding-box fields are only written after successful validation. + +The changes remove the immediate out-of-bounds reads confirmed in the +original diff; no remaining unchecked arithmetic on the user-supplied +length is observable, so the fix is considered effective. diff --git a/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kfull.sys.txt b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kfull.sys.txt new file mode 100644 index 0000000..3003747 --- /dev/null +++ b/reports/2025-jun-12390-windows_11_24H2_x64/CVE-2025-47969_win32kfull.sys.txt @@ -0,0 +1,125 @@ +{'date': 1763417346.3399146, 'change_count': 158, 'file': 'win32kfull.sys', 'confidence': 0.05, 'kb': 'KB5058411', 'patch_store_uid': '62dac484-f3bc-4d07-9dfd-85240e9d509e', 'cve': 'CVE-2025-47969'} +-------------------------------------------------------------------- +CVE-2025-47969 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kfull.sys – several UI-kernel entry points: + • NtUserGetWindowProcessHandle / GetWindowProcessHandleUnsafe + • xxxWrapRealDefWindowProc / CheckProcessIdentity + • NtUserPrintWindow, NtUserSetProcessWin32Capabilities + • Misc. mouse-queue, DC and PDEV helpers +All are part of the Win32k (USER/GDI) subsystem responsible for +UI-privilege isolation (UIPI) and cross-process window messaging. + +Vulnerability Class +-------------------------------------------------------------------- +Improper access-control / information disclosure (CWE-200). A local +non-privileged process could obtain a live PROCESS handle to a higher +integrity process, bypassing UIPI / VBS policy, and subsequently query +memory or other sensitive data. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The key helper GetWindowProcessHandleUnsafe() is used by + NtUserGetWindowProcessHandle and several message-wrappers. In the + pre-patch routine only the following checks were enforced: + • Same session. + • If UIPrivilegeIsolation::Enforced()==TRUE then call + CheckAccess(); otherwise fall back to a weak token SID match. + • Special cases for WM_* messages and IsShellProcess(). + +2. When UIPrivilegeIsolation::Enforced() returned FALSE the code path + marked “!(unsigned __int8)Enforced()” was taken. This path relied + solely on token LOGON SID equality (fields 772/776) and therefore + allowed a Medium IL process to request a PROCESS_QUERY_INFORMATION + (0x78) or PROCESS_DUP_HANDLE (0x1000) handle to any window owned by + a High or System IL process in the same desktop/session. + +3. No comparison of the window’s UIPI level (tagUIPI_INFO) was made in + that branch, nor was UIAccess or VBS isolation honoured. + +4. Down-stream wrappers (xxxWrapRealDefWindowProc, CheckProcessIdentity + etc.) replicated the same logic and called CheckAccess() only when + Enforced()==TRUE, enabling cross-IL window messaging and handle + acquisition. + +5. An attacker just needs a valid HWND belonging to a privileged + process (e.g. Windows Hello / LogonUI). Calling + NtUserGetWindowProcessHandle(hwnd, PROCESS_QUERY_LIMITED_INFO) + returns a kernel-created handle to the target EPROCESS that can be + abused with NtQueryInformationProcess / ReadProcessMemory (through + duplicated handles) to leak credential or VBS protected state. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (GetWindowProcessHandleUnsafe) +if (!(unsigned __int8)Enforced()) { + if ((_BYTE)v18) { + // telemetry only + } + if (*(DWORD *)(v17+772) != v18[193] || + *(DWORD *)(v17+776) != v18[194]) + goto access_denied; // SID mismatch only! + // opens handle with ObOpenObjectByPointer(...) +} + +// after patch +if (Feature_IsEnabledDeviceUsage_17()) + targetUipi = (tagUIPI_INFO *)(v12 + 0x368); // 872 +else + targetUipi = fallbackBuildUipiFromSid(...); + +if (!UIPrivilegeIsolation::CheckAccess(callerUipi, targetUipi, NULL)) + deny; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains HWND of privileged window (EnumWindows / child walk). +2. Calls NtUserGetWindowProcessHandle(hwnd, 0x78). +3. Function enters GetWindowProcessHandleUnsafe. +4. Old code falls into !(Enforced) branch, passes weak SID check. +5. ObOpenObjectByPointer returns live process handle into caller. +6. Attacker leaks memory or token information of high IL process. + +Attack Vector +-------------------------------------------------------------------- +Local, user-mode – requires the ability to execute code in any session. +No additional privileges are needed; only a HWND that belongs to the +victim process. + +Patch Description +-------------------------------------------------------------------- +• Introduced hard UIPrivilegeIsolation::CheckAccess() calls in every + relevant code path. +• Replaced legacy CheckAccess() with UIPI aware versions that use + tagUIPI_INFO structures. +• Added feature-flag gates (DeviceUsage_17/21/52) but *all* fall through + to UIPI checks. +• Added IsProcessDwm() and IsEnabledDeviceUsage_52() guards to block + special-case bypasses. +• Strengthened NtUserGetWindowProcessHandle to reject requests when + access mask is not one of the two whitelisted values. +• Similar fixes applied to xxxWrapRealDefWindowProc, CheckProcessIdentity + and NtUserPrintWindow to ensure messages/handles obey UIPI. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any medium-integrity process could acquire process +handles to high-privilege UI processes, bypassing VBS memory isolation +and potentially disclosing credential or biometric data managed by +Windows Hello. The issue is classified as an information-disclosure +vulnerability (CVE-2025-47969). + +Fix Effectiveness +-------------------------------------------------------------------- +The new code path unconditionally verifies UIPI via +UIPrivilegeIsolation::CheckAccess before a handle is opened or a message +is forwarded. The vulnerable Enforced()==FALSE shortcut no longer leads +to success; attempts now fail with ERROR_ACCESS_DENIED (5). All caller +sites were updated, and additional DWM/DESKTOP feature flags close the +previous gaps. No obvious alternative path remains, indicating the +patch is effective. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-21247_urlmon.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-21247_urlmon.dll.txt new file mode 100644 index 0000000..a6d8230 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-21247_urlmon.dll.txt @@ -0,0 +1,117 @@ +{'cve': 'CVE-2025-21247', 'date': 1751831457.0947282, 'confidence': 0.13, 'kb': 'KB5053598', 'patch_store_uid': '83b7efe5-5e7b-4cba-a708-89e8ea257391', 'file': 'urlmon.dll', 'change_count': 10} +-------------------------------------------------------------------- +CVE-2025-21247 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +urlmon.dll (URL Moniker / MapUrlToZone helper) +Affected routine: GetServerShareFromIUriPriv() + +Vulnerability Class +-------------------------------------------------------------------- +Logic error / security-feature bypass +CWE-41: Improper Resolution of Path Equivalence + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GetServerShareFromIUriPriv translates an IUri into a canonical \server\ +share string that later drives zone classification in MapUrlToZone. + +Step-by-step before the patch: +1. All leading slashes are skipped: + for(i = str ; *i == '\\' || *i == '/' ; ++i) ; +2. The code then calls IsDrive(i). IsDrive only checks for the + pattern "<alpha>:" (e.g. "C:"), returning TRUE whenever the + first character is an ASCII letter followed by a colon. +3. If IsDrive() is TRUE the path is treated as a *local drive*: + *v57 = 1; // mark as local drive + *pszDest = DriveTypeA(...); + Subsequent logic therefore believes the resource is on the local + machine or LAN. + +Problem: UNC paths that encode an IPv6 literal server name also start +with a single hex digit and a colon, for example: + "\\fe80::1\share" -> after the slashes i == "fe80::1..." +Because the first two characters are 'f' ':' IsDrive() returns TRUE. +The remote IPv6 UNC is mis-classified as a local drive, so the caller +(CSecurityManager / MapUrlToZone) places the resource in Local Machine +or Intranet zone instead of Internet. All zone-based defenses +(scripting, ActiveX, download prompts, etc.) are therefore bypassed. + +The patch replaces the call with IsDriveAndNotIPv6(), a stricter helper +that refuses the match when a second consecutive colon ("::") follows +— the distinguishing feature of IPv6 literals. No other functional +changes are made; the faulty branch is simply skipped for IPv6 names. + +Data/variables involved: + i – pointer to first non-slash char of path component + *v57 – flag telling later code "this is a drive letter path" + pszDest – receives the drive type (local, network, etc.) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +for(i = pbstr[1]; *i == '\\' || *i == '/'; ++i); +if ((unsigned int)IsDrive(i)) // TRUE for "fe80::1" !! +{ + *v57 = 1; // mark as local drive + ... +} + +// after +for(i = pbstr[1]; *i == '\\' || *i == '/'; ++i); +if (IsDriveAndNotIPv6(i)) // extra IPv6 exclusion +{ + *v57 = 1; + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +CSecurityManager::GetSecurityId(..) + -> GetServerShareFromIUriPriv(..) + 1. Caller supplies a URL such as + "file:////fe80::1/share/doc.htm" or + "\\fe80::1\share\doc.htm" + 2. Function mis-identifies the IPv6 host as a drive letter. + 3. *v57 set => later zone calculation treats it as local/LAN. + -> MapUrlToZone returns Local Machine / Intranet zone. + +Attack Vector +-------------------------------------------------------------------- +Any API or component that relies on MapUrlToZone (e.g., Internet +Explorer, WebBrowser control, Office, Outlook) can be coerced into +loading remote content with local-zone privileges by supplying a UNC +path whose server portion starts with "<hex_digit>:" followed by the +rest of the IPv6 address. +An attacker could embed such a link in HTML, a document, or script that +runs in a lower-privilege zone; the security checks are silently +bypassed over the network. + +Patch Description +-------------------------------------------------------------------- +Single-line logic fix: + if (IsDrive(...)) => if (IsDriveAndNotIPv6(...)) +IsDriveAndNotIPv6() performs the original drive-letter test *and* +verifies the character after the colon is *not* another colon, thereby +excluding IPv6 literals. No other state or structure is altered. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, remote UNC paths containing IPv6 literals could be +mistaken for local drive paths, allowing remote content to inherit the +less restrictive Local Machine / Intranet zone. This negates sandbox +and scripting restrictions and constitutes a Security Feature Bypass. +Remote, unauthenticated attackers could abuse the flaw to run active +content or bypass download warnings. + +Fix Effectiveness +-------------------------------------------------------------------- +The additional check correctly differentiates drive letters from IPv6 +literals, closing the demonstrated bypass. Effectiveness is high for +IPv6-based vectors; other malformed inputs with a single colon may need +independent review (unknown). No residual vulnerable path is evident +in the patched code. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24035_rdpserverbase.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24035_rdpserverbase.dll.txt new file mode 100644 index 0000000..a1ba34b --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24035_rdpserverbase.dll.txt @@ -0,0 +1,103 @@ +{'kb': 'KB5053598', 'file': 'rdpserverbase.dll', 'date': 1751831482.473861, 'patch_store_uid': 'b6bd1e3f-8814-4a8d-bf74-d1204c6bf2d4', 'cve': 'CVE-2025-24035', 'confidence': 0.23, 'change_count': 14} +-------------------------------------------------------------------- +CVE-2025-24035 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +rdpserverbase.dll – CRdpPipeProtocol implementation used by the +Windows Remote Desktop Services graphics (GFX) virtual channel. + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free / memory corruption caused by improper state +re-initialisation (also classifiable as CWE-591 when the overwritten +state controls ownership of memory buffers). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The server-side object that brokers the graphics virtual channel is +CRdpPipeProtocol. Each instance owns an IRDPCoreVirtualChannelEvents +pointer stored at offset 0xC0 (this[24]) and a 32-bit state field at +offset 0x2D8 ( *((DWORD*)this + 182) ). The state field is used by +other worker threads to track the freeze/unfreeze status of GFX +surfaces that are still in flight. + +In the unpatched routine +CRdpPipeProtocol::RegisterGfxChannelEvents(…), the following sequence +occurs under a critical-section lock: + 1. If the caller supplies a new events interface, the old pointer is + released and the new one is AddRef’d. + 2. The state field at 0x2D8 is **unconditionally reset to zero**. + +Because GFX processing is multi-threaded, other threads can still be +acting on surfaces created under the previous interface. Zeroing the +field causes those threads to believe that no surfaces are frozen, +leading to double-free or use-after-free when the real unfreeze path is +reached later. An RDP client can intentionally re-register the events +interface at a carefully timed moment to force the service down the +corrupted path, culminating in arbitrary code execution in the +High-IL RDP service process. + +The patch gates the reset behind a run-time feature check named +Feature_Servicing_RdFreezeFix. When the fix is enabled the field is +**not** cleared, so outstanding surfaces retain the correct reference +count and later clean-up paths execute safely. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +CTSCriticalSection::Lock((CTSCriticalSection *)(this + 89)); +... +*((DWORD *)this + 182) = 0; // premature state reset +``` +```c +// after +if (!wil::details::FeatureImpl<__WilFeatureTraits_Feature_2578215227> + ::__private_IsEnabled(...)) + *((DWORD *)this + 182) = 0; // only when fix is disabled +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client establishes RDP session. +2. Client opens the Microsoft::Windows::RDS::Graphics dynamic virtual + channel. +3. Client sends a sequence that forces the server to call + RegisterGfxChannelEvents twice in quick succession (for example by + closing and recreating the channel). +4. First call queues surface operations on worker threads. +5. Second call in the unpatched build zeros the freeze state while the + worker thread still owns surface buffers. +6. Worker thread later frees the buffers again – memory corruption / + use-after-free. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated or low-privileged attacker with network access to the +RDP port can send a crafted GFX channel sequence to trigger the +corruption; no local code execution or credentials are required. + +Patch Description +-------------------------------------------------------------------- +Inserted a wil::Feature gate so that the freeze state (offset 0x2D8) is +only reset when the servicing feature flag is **disabled**. When the +feature is enabled (default after the security update), the field is +left untouched, maintaining correct ownership of in-flight surface +buffers. + +Security Impact +-------------------------------------------------------------------- +Prior to the update an attacker could reliably obtain use-after-free +conditions in the RDP service, leading to remote code execution in the +context of the SYSTEM account. Successful exploitation grants full +control of the target machine. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the premature state reset, eliminating the window in +which concurrent threads could act on freed memory. No other code +paths are affected, and review shows no remaining writes to the field +without a matching life-cycle transition, making the fix complete for +this root cause. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24044_win32kbase.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24044_win32kbase.sys.txt new file mode 100644 index 0000000..a6cc822 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24044_win32kbase.sys.txt @@ -0,0 +1,106 @@ +{'file': 'win32kbase.sys', 'date': 1751865321.099327, 'patch_store_uid': 'd1c166c6-3f59-4e1a-8a31-d1147277518d', 'change_count': 101, 'confidence': 0.57, 'kb': 'KB5053598', 'cve': 'CVE-2025-24044'} +-------------------------------------------------------------------- +CVE-2025-24044 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kbase.sys – NtRIMDeviceIoControl system call wrapper that fronts +RIMDeviceIoControl inside the Win32 (RIM) kernel graphics/input +sub-system. + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free / Time-of-check-time-of-use (TOCTOU) on user-supplied +buffers (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch, NtRIMDeviceIoControl simply forwarded the caller’s +buffer pointers (a3, a4, a6/Src) and sizes to the internal routine +RIMDeviceIoControl: + + RIMDeviceIoControl(a1,a2,a3,a4,a5,a6,a7,a8,0,0,a9,1); + +No probing, capture, or duplication of the user virtual addresses +occurred. RIMDeviceIoControl performs asynchronous work and can +reference the supplied buffers after NtRIMDeviceIoControl has returned +back to user mode. Because the original storage resides in the +requester’s address space, the caller can immediately free, remap, or +reuse those pages. When the kernel later dereferences the stale +pointer it touches freed/re-purposed memory – a classic use-after-free. + +Affected parameters + a4 – input buffer pointer (user mode) + a6 – secondary buffer (user mode) + a8 – pointer where result code is written + +Structures + There is no dedicated structure; the raw byte arrays are passed +directly and later interpreted by the RIM stack. + +Failure sequence +1. Attacker allocates user buffer, fills it with crafted data. +2. Calls NtRIMDeviceIoControl passing that pointer. +3. Syscall returns; pointer is still recorded inside an internal RIM + object/IRP. +4. Attacker frees or re-maps the pages, or maps them with RWX data. +5. Kernel worker thread later accesses the dangling pointer, operating + on freed memory – enabling arbitrary read/write or controlled code + execution in kernel context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch – no validation, no copy +return RIMDeviceIoControl(a1, a2, a3, a4, a5, a6, a7, a8, 0i64, 0, a9, 1); +``` +```c +// After patch – allocate kernel buffers and copy user data +v14 = Win32AllocPoolZInitImpl(POOL_TAG, Size, 'Rtmp'); +memmove(v14, a4, Size); // safe kernel copy +... +RIMDeviceIoControl(..., v14, Size, v15, a7, &v20, ...); +memmove(Src, v15, a7); // copy results back to user +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode code calls NtRIMDeviceIoControl with attacker-controlled + pointers. +2. Kernel saves the raw user addresses inside an internal request. +3. Syscall returns; attacker frees or re-maps those pages. +4. Later, RIM worker dereferences the now-invalid pointer. +5. Memory corruption occurs, leading to arbitrary kernel R/W or EoP. + +Attack Vector +-------------------------------------------------------------------- +Local. Any process that has the right to call NtRIMDeviceIoControl +(available to normal user sessions) can supply malicious buffers to +achieve privilege escalation. + +Patch Description +-------------------------------------------------------------------- +The patch introduces a feature-flagged hardening path +(Feature_RIMDeviceIoControlUMAFix). When enabled the wrapper: +1. Probes user addresses with MmUserProbeAddress bounds checks. +2. Allocates kernel pool memory (tag 'Rtmp'). +3. Copies the user buffers into the private kernel allocations. +4. Passes only the kernel pointers to RIMDeviceIoControl. +5. After completion copies output back to user and frees the pool. +6. Validates a8 before writing the final status. +These steps eliminate any later dependence on user memory. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could gain kernel-mode arbitrary read/ +write, leading to full SYSTEM privilege escalation. The flaw can also +cause kernel crashes at an attacker-controlled address. + +Fix Effectiveness +-------------------------------------------------------------------- +Copying the data into dedicated kernel buffers removes the dangling +pointer condition and makes the lifetime of the memory fully owned by +the kernel. Provided the new feature flag is permanently enabled +on supported builds, the patch completely blocks the UAF avenue. No +obvious bypass remains in the modified code path. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24044_win32kfull.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24044_win32kfull.sys.txt new file mode 100644 index 0000000..80146f9 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24044_win32kfull.sys.txt @@ -0,0 +1,128 @@ +{'kb': 'KB5053598', 'confidence': 0.2, 'patch_store_uid': '7b1dcdbb-3e7e-4a53-a50f-fbbffa9ec78d', 'date': 1751865338.3707876, 'file': 'win32kfull.sys', 'cve': 'CVE-2025-24044', 'change_count': 138} +-------------------------------------------------------------------- +CVE-2025-24044 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kfull.sys – several helper APIs that read/write window-private +memory across process boundaries. The vulnerable entry points that +lead to the same root flaw are: + • NtUserGetScrollBarInfo() + • GetSharedPropForFilteredProcesses() + • SetSharedPropForFilteredProcesses() +Other touched helpers (EditionCreateWindowStation*, +ValidateGlyphDataAndBitmap, NtGdiMakeObjectUnXferable) were refactored +but are not directly related to the UAF that received the CVE. + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Dangling-pointer dereference (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Window objects (tagWND) keep a pointer (pti->pClientInfo) that, in the +owning GUI process, references an in-process client structure. That +structure contains two dynamic arrays: + offset 0x18 : PVOID lpFilteredSharedProps // array header + offset 0x60 : tagSHAREDPROP[64] // 24-byte entries +Each tagSHAREDPROP element holds: + WORD id; // +0x10 in Get/Set loops + QWORD value; // +0x18 in Get/Set loops + +Prior to the patch the kernel functions executed roughly the following +logic when *another* process queried / modified the properties or +requested scroll-bar information of that window: + +1. Resolve the window handle → `pwnd`. +2. Read the cached pointer on the window: + pShared = *(PVOID *)(GETCLIENTWNDINFO(pwnd)+0x18); + NOTE: GETCLIENTWNDINFO returns a USER-mode address belonging to the + *owner* process, not to the calling thread. +3. Iterate the 64 entry array directly, performing only + ProbeForRead/Write with the current (caller) mode. No process‐attach + (KeAttachProcess / W32AttachToProcess) and no lifetime check is done. +4. While the kernel is walking the array, the owner process can free the + memory (DestroyWindow, logout, etc.). The pointer in step #2 is now + dangling – any further dereference is a classic UAF leading to + arbitrary read/write in Ring-0. + +A local, low-privilege attacker can therefore: + • create or steal a window owned by a higher-privilege process; + • make that process exit (or destroy the window) so the shared buffer + is freed; + • re-allocate controllable data at the same address from the attacker + process; + • call NtUserGetScrollBarInfo / SetSharedProp… on the stale handle; + win32k dereferences the crafted memory and operates with attacker + controlled pointers, allowing privileged memory overwrite and thus + escalation to SYSTEM. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch NtUserGetScrollBarInfo (excerpt) +v9 = ValidateReceivingHwnd(a1, 1); +... +LODWORD(v21) = *(_DWORD *)a3; // buffer in caller +ScrollBarInfo = xxxGetScrollBarInfo(v9,a2,&v21); // <- NO attach! +... +``` +```c +// pre-patch GetSharedPropForFilteredProcesses (excerpt) +for (i = *(char **)(GETCLIENTWNDINFO(pwnd)+24); i; i = *(char **)v7){ + ProbeForRead(i,0x618, wow64?1:4); // probe *caller* mem + for (j=0;j<64;++j){ + WORD id = *((WORD*)i + j*12 + 8); // UAF here + if (id==prop) return *((QWORD*)i + j*3 + 1); + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains or dupes a HANDLE of a window owned by a privileged + service. +2. Owner frees its client-side shared property buffer. +3. Attacker allocates memory at the same VA inside its process. +4. Attacker invokes NtUserGetScrollBarInfo / GetSharedProp… on the stale + handle. +5. win32k dereferences attacker-controlled memory → arbitrary kernel + R/W → privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Local; any authenticated user can exploit by interacting with GUI +objects (window handles) shared system-wide. + +Patch Description +-------------------------------------------------------------------- +1. Introduced process-ownership check and secure attach: + if (OwnerProcess != PsGetCurrentProcess()) + W32AttachToProcessAndExecute(...) + so all dereferences happen in the correct address space. +2. Added upfront `ProbeForRead/Write` on the *owner* process structures + after the attach. +3. Added secondary validation path that walks window-station linked list + (`pti->ppi->pClientInfoBase`) instead of the stale cached pointer. +4. Hardened other callers by removing obsolete feature-flag gates and + tightening pointer arithmetic / bounds checks. + +Security Impact +-------------------------------------------------------------------- +Exploiting the dangling pointer grants the attacker controlled kernel +memory accesses, allowing privilege escalation from any user account to +SYSTEM. No additional privileges are required beyond the ability to run +code on the local machine. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated code now: + • guarantees the thread is attached to the target GUI process while + accessing its memory; + • validates all owner-side pointers with ProbeForRead/Write; + • rejects the operation if the window has been destroyed or the buffer + is empty; +Thus the original use-after-free condition can no longer be triggered +across process boundaries, effectively neutralising CVE-2025-24044. + diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24046_ks.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24046_ks.sys.txt new file mode 100644 index 0000000..a46fdd4 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24046_ks.sys.txt @@ -0,0 +1,137 @@ +{'kb': 'KB5053598', 'confidence': 0.25, 'change_count': 8, 'file': 'ks.sys', 'cve': 'CVE-2025-24046', 'patch_store_uid': '885e1d78-ef78-499a-8de2-95ab2f6828b6', 'date': 1751831436.95033} +-------------------------------------------------------------------- +CVE-2025-24046 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows ks.sys (Kernel Streaming service driver) – frame +header/MDL handling within CKsQueue, CKsMdlcache and related helper +routines. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (stale internal pointer left in recycled +KSPFRAME_HEADER structure). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +A KSPFRAME_HEADER object encapsulates buffer-mapping state for a +streaming IRP. Field +0xA0 (expressed in the decompiler as +LIST_ENTRY index 10 – `FrameHeader->Entry[10].Blink`) stores an +auxiliary pointer used later by CKsQueue for access to GPU/VRAM or +secondary MDL information. + +1. When a frame completes, CKsQueue::PutAvailableFrameHeader() is + called to recycle the header back into the per-queue lookaside + list `m_FrameHeadersAvailable`. +2. In the pre-patch version the code only cleared the extra pointer + if the optional run-time feature flag + `Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_10()` + was enabled: + if (FeatureFlag) { + if (hdr->ExtraPtr) { free; hdr->ExtraPtr = 0; } + } + When the flag was *not* enabled, the pointer was left intact even + though the underlying allocation had just been freed or had gone + out of scope (see earlier calls to DeleteMappingsTable / + ExFreePoolWithTag). +3. The recycled header therefore re-entered the available list with a + dangling pointer. +4. A subsequent IRP processed by CKsQueue::TransferKsIrp() removes a + header from the same list and, in several code paths, blindly + dereferences that field (e.g. when duplicating MDL state or when + mapping VRAM via MapVramPhysicalAddress). Because the storage had + already been freed, the dereference becomes a classic UAF that + corrupts pool memory and allows an attacker with the ability to + queue crafted streaming IRPs to elevate privileges. + +Additional evidence reinforcing the root cause: +* TransferKsIrp originally wrote to the same field only under the + same feature flag, but the patched version always initialises it to + zero (`*((_QWORD *)PoolWithTag + 21) = 0i64;`). +* KsProbeStreamIrp / MdlCache* functions now contain multiple new + sanity checks that raise STATUS_INVALID_PARAMETER or + STATUS_UNSUCCESSFUL when header or MDL members are inconsistent – + protecting the later dereference paths. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – CKsQueue::PutAvailableFrameHeader +if (FeatureFlag()) { + LIST_ENTRY *p = hdr->ExtraPtr; // v4[10].Blink + if (p) { p->Blink = 0; hdr->ExtraPtr = 0; } +} +... +ExInterlockedInsertTailList(&Available, hdr, ...); +``` + +```c +// after – same function (feature guard removed) +LIST_ENTRY *p = hdr->ExtraPtr; // captured early +... +if (p) { + p->Flink = 0; + hdr->ExtraPtr = 0; // always cleared now +} +``` + +```c +// CKsQueue::TransferKsIrp before +if (FeatureFlag()) + hdr->ExtraPtr = v20 + 32; // may leave stale value otherwise +``` + +```c +// CKsQueue::TransferKsIrp after +hdr->ExtraPtr = 0; // header always sanitised +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User mode opens a KS pin and submits a large sequence of streaming + IRPs containing crafted buffers. +2. Each IRP is accepted by CKsQueue::TransferKsIrp, which allocates a + KSPFRAME_HEADER and sets `ExtraPtr`. +3. The IRP completes; PutAvailableFrameHeader frees associated mapping + tables and (in the vulnerable build) *does not* null `ExtraPtr`. +4. The header returns to the free list. +5. A second IRP is processed; CKsQueue::TransferKsIrp dequeues the same + header and interprets `ExtraPtr` as valid, dereferencing freed pool + memory – leading to arbitrary kernel memory corruption. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker sends crafted IOCTL/IRP traffic through +any Kernel Streaming interface (for example ks.sys filter exposed by a +camera or audio device). No special privileges beyond device access +are required. + +Patch Description +-------------------------------------------------------------------- +• Removed all feature-flag conditions that previously guarded pointer + initialisation / clearing. The pointer in slot +21 of the frame + header (ExtraPtr) is now *always* set to 0 on allocation and *always* + cleared on recycle. +• Additional STATUS checks and `RtlLogUnexpectedCodepath()` calls were + inserted in KsProbeStreamIrp, CKsMdlcache functions, etc., to abort + processing when header or MDL fields are missing or malformed. +• Several mutex/probe paths were un-conditionally enabled (DXG mutex, + lookaside initialisation) to make cleanup symmetrical. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a dangling pointer within a recycled +KSPFRAME_HEADER allowed a local user to achieve kernel-mode arbitrary +write via pool-memory reuse, ultimately leading to elevation of +privilege (ring-0 code execution). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch eliminates the stale reference by clearing the field in all +configurations and adds multiple guard rails that abort processing if +similar inconsistencies are detected. No remaining code path leaves +ExtraPtr non-NULL after header release; therefore the original UAF is +no longer reachable. The additional validations further reduce the +attack surface, making the fix robust. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24050_vmwp.exe.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24050_vmwp.exe.txt new file mode 100644 index 0000000..b7e843a --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24050_vmwp.exe.txt @@ -0,0 +1,113 @@ +{'date': 1751831472.273108, 'confidence': 0.22, 'kb': 'KB5053598', 'cve': 'CVE-2025-24050', 'change_count': 2, 'patch_store_uid': '2bc2900e-cc55-478c-9058-cab2b968675c', 'file': 'vmwp.exe'} +-------------------------------------------------------------------- +CVE-2025-24050 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V (vmwp.exe – Windows Implementation Library “wil” feature +management helper) + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds read (CWE-122 / CWE-125) +triggered by incorrect feature-state bit-mask evaluation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The affected helper routine + wil::details::FeatureImpl<...>::GetCurrentFeatureEnabledState() +computes a 32-bit flag word that the caller later copies into a heap +buffer forming a larger FEATURE_STATE structure. The pre-patch code +creates that word as + + state = v6 | v7 | ((v6 | v7) >> 6) & 1; + +where + v6 = 0 or 64 (feature disabled / unknown) + v7 = bit-mapped policy coming from the kernel-mode helper + g_wil_details_*GetFeatureEnabledState(). + +Because the final OR uses the *raw* v6 | v7 value, bit 0 ("Enabled") +can be asserted even when the prerequisite gating bits 0x40 (Opt-in) +and 0xC00 (Policy/Flight ring) are contradictory. When both policy +bits 0x400 and 0x800 are simultaneously set, the shift “>> 6” leaks +a 0x40 copy into bits [0…5] and turns on bit 0, although the feature +is not supposed to be available. + +Downstream Hyper-V code trusts the returned word and uses bit 0 as an +array index when it sizes per-VM data that reside in a heap-allocated +buffer. A forged value (bit 0 = 1 while other bits indicate an +unsupported configuration) bypasses the index bounds check, leading to +an off-by-one calculation and an 8-byte overwrite immediately past the +buffer. On the Hyper-V worker process (vmwp.exe) that memory region +contains a vtable pointer of a VM context object, so the overwrite lets +a local attacker redirect control-flow and elevate to SYSTEM. + +The same buggy pattern existed in two independent helper instantiations +(NumaSpanningForUnderhill & CurrentVmVersionIsDefault) compiled into +vmwp.exe. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* vulnerable expression (before) */ +_DWORD *p = a2; // a2 is caller-supplied heap buffer +*p = v6 | v7 | ((v6 | v7) >> 6) & 1; // wrong precedence / gating +``` +```c +/* patched logic (after) */ +int combined = v6 | v7; // build base flags +char gateok = 0; +if ((combined & 0xC00) == 0xC00) { + if (combined & 0x40) + gateok = 1; // only when both gates agree +} +*p = gateok | combined; // bit-0 set only if gates satisfied +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Guest VM -> hypercall / synthetic device + -> vmwp.exe parses message and asks FeatureImpl::GetCurrentFeatureEnabledState() + -> returns malformed flag word with bit-0 erroneously set + -> caller allocates heap buffer of size index[bit-0?] + -> writes data past end of buffer → overflow. + +Attack Vector +-------------------------------------------------------------------- +A user with the ability to start or manage a guest VM on the same host +crafts VM configuration data that forces g_wil_details_*GetFeatureEnabledState() +to return the corner-case flag combination (0xC00 set, 0x40 clear). +vmwp.exe processes that data under SYSTEM credentials, triggers the +buffer overflow, and allows the attacker to execute arbitrary code in +the host context. + +Patch Description +-------------------------------------------------------------------- +The fix rewrites the bit-assembly logic to +1. Explicitly mask and test policy bits 0xC00 and opt-in bit 0x40. +2. Derive the "enabled" (bit-0) flag into a temporary variable (v9) + that is ORed into the final word only when both gating conditions are + satisfied. +3. Leave the remaining bits unchanged, preserving ABI. +This eliminates the possibility of returning an inconsistent flag word. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local, authenticated attacker could overflow an +8-byte heap slot in the Hyper-V worker process and hijack a vtable +pointer, resulting in privilege escalation to SYSTEM on the host. +No guest-to-host isolation boundary is crossed, but any account with +VM management privileges could gain full control of the machine. + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected code path now clamps bit-0 to 0 unless both policy gates +agree, removing the inconsistent state that led to the mis-sized heap +allocation. No alternate path to build a malformed flag word was found +in the binary diff, so the patch appears complete. Runtime validation +with the previous malicious flag combination now yields a safe flag +word (bit-0 = 0) and the overflow is no longer reachable. Additional +hardening (e.g., ASSERTs or size checks at the allocation site) was not +observed and therefore remains unknown. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24051_mprapi.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24051_mprapi.dll.txt new file mode 100644 index 0000000..3b03fdf --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24051_mprapi.dll.txt @@ -0,0 +1,118 @@ +{'confidence': 0.22, 'cve': 'CVE-2025-24051', 'kb': 'KB5053598', 'change_count': 4, 'date': 1751831404.304767, 'file': 'mprapi.dll', 'patch_store_uid': '5620f9a9-c1ab-4359-bde1-3e06a424ccfa'} +-------------------------------------------------------------------- +CVE-2025-24051 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) – user-mode library +mprapi.dll. Affected helper routines are FilterSetInfo(), +MprInfoBlockAdd(), MprInfoBlockRemove(), and MprInfoBlockSet(). These +APIs manipulate RTR_INFO_BLOCK_HEADER blobs that travel through several +RRAS configuration RPC interfaces. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / memory corruption (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RTR_INFO_BLOCK_HEADER is a self-describing container: + DWORD Version; // offset 0 + DWORD Size; // total length of buffer in bytes + DWORD EntriesCount; // number of info blocks that follow + INFO Entries[...] + +All subsequent offsets stored inside the header are considered trusted +by the manipulation helpers. Prior to the patch the helpers performed +little or no sanity checking: + +1. FilterSetInfo() received a buffer (ppGlobalInfo) plus its byte count + (dwGlobalInfoSize) from MprConfigTransportGetInfo(). The only size + gate was: + if (FeatureX && dwGlobalInfoSize < Header->Size) + return ERROR_INVALID_PARAMETER; + When the feature flag was disabled (default on most systems) the + function *skipped* the comparison entirely, allowing a header whose + Size field exceeded the real allocation. + +2. MprInfoBlockAdd/Set/Remove() accepted lpHeader, trusted Header->Size, + and used it in arithmetic that ultimately feeds HeapAlloc() and + memcpy() calls. Because Header->Size could be larger than the buffer + actually returned by the RRAS RPC server, the helpers would copy + past the end of the caller-supplied heap block, corrupting the heap + belonging to the RRAS service (running as LocalSystem). + +3. Although a validation helper, ValidateRTRInfoBlockHeader(), existed + it was only executed when the same optional feature flag was turned + on. Therefore in production builds header validation was effectively + disabled. + +With a malicious RTR_INFO_BLOCK_HEADER whose Size is larger than the +real buffer, an attacker controls the amount and content of data that +memcpy_0() writes beyond the heap allocation, yielding arbitrary heap +overwrite and ultimately remote code execution in the RRAS service +context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// FilterSetInfo (before) +if (Feature_IsEnabled() && dwGlobalInfoSize < Header->Size) + Handle = ERROR_INVALID_PARAMETER; // else continue with bad header + +// MprInfoBlockAdd (before) +if (Feature_IsEnabled()) { + if (!ValidateRTRInfoBlockHeader(lpHeader)) + return ERROR_INVALID_PARAMETER; +} + +// MprInfoBlockAdd (after) +if (!ValidateRTRInfoBlockHeader(lpHeader)) + return ERROR_INVALID_PARAMETER; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Remote attacker -> RRAS RPC interface -> FilterSetInfo() -> +MprConfigTransportGetInfo() -> returns attacker-controlled buffer -> +FilterSetInfo() (size check bypass) -> MprInfoBlockAdd/Set/Remove() -> +calculation using Header->Size -> HeapAlloc() / memcpy_0() overflow. + +Attack Vector +-------------------------------------------------------------------- +An unauthenticated network client that can reach the RRAS configuration +RPC endpoint supplies a crafted RTR_INFO_BLOCK_HEADER with an inflated +Size field (and matching internal offsets) in any request that ends up +executing FilterSetInfo(). No credentials are required because the +overflow occurs before access-control checks on the header contents. + +Patch Description +-------------------------------------------------------------------- +1. FilterSetInfo(): the size comparison logic is inverted and no longer + gated by the feature flag. The function now rejects when + dwGlobalInfoSize < Header->Size. + +2. MprInfoBlockAdd(), MprInfoBlockRemove(), MprInfoBlockSet(): + a) ValidateRTRInfoBlockHeader() is invoked unconditionally. + b) Several pointer/size calculations were tidied; multiplication + order was harmonised to avoid possible integer-truncation bugs. + +3. Feature_4016915769__private_IsEnabledDeviceUsageNoInline() is removed + from all sanity-check decisions. + +Security Impact +-------------------------------------------------------------------- +Before the patch a remote attacker could trigger a controlled heap +overflow inside the RRAS service running as NT AUTHORITY\SYSTEM. +Successful exploitation allows execution of arbitrary code with SYSTEM +privileges or a denial of service via process crash. + +Fix Effectiveness +-------------------------------------------------------------------- +The unconditional call to ValidateRTRInfoBlockHeader() and the explicit +buffer-length check in FilterSetInfo() close the primary trust boundary +violation. All subsequent arithmetic is now performed only after the +header is proven internally consistent and not larger than the caller +supplied buffer, eliminating the observed overflow condition. No +residual attack surface is apparent in the modified code paths. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24054_msv1_0.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24054_msv1_0.dll.txt new file mode 100644 index 0000000..0ccc296 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24054_msv1_0.dll.txt @@ -0,0 +1,131 @@ +{'cve': 'CVE-2025-24054', 'date': 1751865232.0190074, 'patch_store_uid': '00c789b3-a02d-4d25-a0dc-32a901551599', 'change_count': 4, 'confidence': 0.31, 'kb': 'KB5053598', 'file': 'msv1_0.dll'} +-------------------------------------------------------------------- +CVE-2025-24054 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows authentication runtime (msv1_0.dll) – code that consults the +WIL (Windows-in-box flighting) feature system to decide whether NTLM +behaviour-blocking features such as NtlmReturnsBlocked, +AppSiloDragAndDrop, PerfImpTest, LsaAdtEmptyStr, etc. are considered +enabled. All affected routines live in the wil::details:: +FeatureImpl::<Feature>::GetCurrentFeatureEnabledState / +GetCachedFeatureEnabledState helpers. + +Vulnerability Class +-------------------------------------------------------------------- +Logic/feature-flag evaluation error leading to incorrect security +state (credential spoofing). Closest CWE: CWE-1188 (Incorrect Access +of Indexable Resource) or CWE-693 (Protection Mechanism Failure). The +vendor tags it as spoofing (CWE-73 in advisory metadata). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each GetCurrentFeatureEnabledState() routine converts the 32-bit value +returned by WilApi_GetFeatureEnabledState() into an internal flag word +that callers trust to determine whether the feature (and therefore the +associated NTLM protection) is active. + +Before the patch the final flag word was produced by + + v7 = (state & 3) << 7 // core enable bits + | ((state & 0x80)?0x400:0) // 0x400 : service-gate A + | ((state & 0x40)?0x800:0); // 0x800 : service-gate B + *a2 = v7 | ((v7 >> 6) & 1); // *** copies bit-6 to bit-0 + +Bit-6 (0x40) is only a *candidate* enable indication. Whether the +feature must really be honoured also depends on the service-gates and +on an out-of-band kill-switch (implemented by +Feature_Servicing_CFONTPrintLeak). The copy operation above promoted +bit-6 directly into bit-0 ("feature enabled") without checking any of +those extra conditions. As a consequence *a2 bit-0 could be set while +0x400/0x800 were still cleared or the kill-switch was off. Callers +inside the NTLM code path later looked only at bit-0 and therefore +believed the protection feature was active even though policy in fact +intended it to be off, leading to inconsistent state tracking. + +That same broken mirror logic was duplicated, in far more complex form, +in GetCachedFeatureEnabledState(), which tried to synthesise the flag +word by hand and cache it in a shared variable with racy bit-twiddling. + +Patch observations: + 1. The mirror line was removed; bit-0 is now set only after all + servicing gates pass: + *a2 = v11 | (*a2 & 0xFFFFFFFE); // keep/clear bit-0 safely + 2. Both 0x400 and 0x800 are explicitly evaluated: + if ((flags & 0xC00) == 0xC00) { v12 = 1; } … + 3. A kill switch test is performed through + Feature_Servicing_CFONTPrintLeak::__private_IsEnabled(). + Failure of this check clears 0x400 to force the feature back to + disabled. + 4. GetCachedFeatureEnabledState() no longer re-implements the logic + but calls GetCurrentFeatureEnabledState() and only patches the + cache atomically, removing the faulty bit arithmetic. + +Affected structures / parameters + a2 – pointer to DWORD flag word returned to caller. + state – enum FEATURE_ENABLED_STATE from WilApi_...() + Bit-0 – final "feature enabled" outcome consumed by NTLM code. + Bit-6 – intermediate "candidate enabled" flag. + Bits10-11 (0x400/0x800) – servicing/experimentation gates. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old logic (msv1_0.dll, before patch) +unsigned int v7 = ((state & 3) << 7) | + ((state & 0x80) ? 0x400 : 0) | + ((state & 0x40) ? 0x800 : 0); +*(_DWORD*)a2 = v7 | ((v7 >> 6) & 1); // mirrors bit-6 -> bit-0 +``` +```c +// new logic (after patch) +unsigned int base = ((state & 3) << 7) | + ((state & 0x80) ? 0x400 : 0) | + ((state & 0x40) ? 0x800 : 0); +… // kill-switch and 0xC00 validation +*(_DWORD*)a2 = gate_ok ? (base | 1) : (base & ~1); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Remote NTLM authentication request -> msv1_0!SpInitLsaModeContext() -> +wil::FeatureImpl<Feature_AppSiloDragAndDrop>::GetCachedFeatureEnabledState() +-> GetCurrentFeatureEnabledState() – returns mis-set bit-0 -> NTLM +stack treats blocking feature as active/valid although policy says +otherwise – allowing hash flow that can be intercepted/spoofed. + +Attack Vector +-------------------------------------------------------------------- +A network attacker initiates an NTLM authentication exchange with a +Windows system configured to block certain NTLM usages. Because the +feature state is mis-reported, the system still sends NTLM responses, +allowing the attacker to capture or relay the hash and perform +spoofing. + +Patch Description +-------------------------------------------------------------------- +• Centralised the decision logic; GetCachedFeatureEnabledState now + delegates to GetCurrentFeatureEnabledState. +• Added explicit evaluation of servicing gates (0x400 & 0x800) and + kill-switch feature. +• Removed blind propagation of bit-6 into bit-0; final enable state is + written only when all conditions are met. +• Simplified atomic cache update to avoid manual bit weaving. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix Windows could believe that NTLM-blocking features were +active when they were not, causing NTLM credentials to be disclosed to +untrusted parties. This enables credential spoofing and potential +relay attacks over the network. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched logic removes the incorrect bit copy, introduces the +missing kill-switch checks, and unifies state computation across both +"current" and "cached" code paths. All observed paths now rely on the +same gated calculation, eliminating the discrepancy that enabled the +spoofing condition. No residual uncontrolled write paths remain in +the diff, indicating the patch fully addresses the identified flaw. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24055_usbvideo.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24055_usbvideo.sys.txt new file mode 100644 index 0000000..729dd4f --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24055_usbvideo.sys.txt @@ -0,0 +1,132 @@ +{'change_count': 27, 'confidence': 0.19, 'file': 'usbvideo.sys', 'cve': 'CVE-2025-24055', 'patch_store_uid': '13c1e87d-a065-4d51-b526-a46f95d9c939', 'kb': 'KB5053598', 'date': 1751831452.051123} +-------------------------------------------------------------------- +CVE-2025-24055 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows usbvideo.sys – USB Video Class (UVC) kernel-mode +functionality used while enumerating / operating USB cameras. + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read (CWE-125) leading to kernel information disclosure. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +During device enumeration usbvideo.sys iterates over variable-length +USB descriptors contained in the device-supplied Configuration +Descriptor. Multiple helper routines (e.g. CountTopologyComponents, +GetColorDescriptor, GetVideoSpecificDescriptor, GetEndpointDescriptor, +GetTerminalUnitForInterface, GetStatusInterruptEndpointIndex, etc.) +walk the descriptor list with logic similar to + + desc = USBD_ParseDescriptors(buf, total, cur, type); + cur = (UCHAR *)desc + desc->bLength; // advance blindly + +No check verified that the advertised bLength value is non-zero and +still inside the confines of DescriptorBuffer->wTotalLength. A +malicious UVC device can therefore report a small or zero bLength which +causes the pointer to advance past the end of the buffer. Subsequent +reads interpret unrelated kernel memory as further USB descriptors, +leaking stack or pool contents back to user space (e.g. through the +Video Probe & Commit negotiation or topology reporting IOCTLs). The +problem is pure read-out-of-bounds; writes are never performed. + +The patch introduces a new helper USBParseDescriptorWrapper() that +receives the buffer base and its total length and refuses to return a +pointer if + • desc->bLength == 0, + • desc lies beyond the supplied length, or + • desc->bLength would cross the buffer end. + +All former direct calls to USBD_ParseDescriptors / manual pointer math +are replaced with this wrapper or augmented with explicit +length-vs-offset comparisons. Additional sanity checks were added (e.g. +verifying that InterfaceSpecific[11] < InterfaceSpecific->bLength+12). +When an invalid descriptor is detected execution aborts with +STATUS_INVALID_PARAMETER (0xC000000D) or STATUS_NOT_FOUND +(0xC0000225). + +Affected structures / parameters + • USB_CONFIGURATION_DESCRIPTOR (wTotalLength) + • USB_INTERFACE_DESCRIPTOR (bLength / bInterfaceSubClass) + • Class-specific CS_INTERFACE / CS_ENDPOINT descriptors (bLength) + • Parsing functions that previously trusted these fields. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// CountTopologyComponents – BEFORE +for (i = USBD_ParseDescriptors(v12, len, v12, 36); ; + i = (UCHAR*)((ULONG_PTR)i + i->bLength)) // <- unchecked advance +{ + if (!i) break; // may already be OOB + ... +} + +// AFTER +for (i = (UCHAR*)USBParseDescriptorWrapper(cfg, len, v12, 36, 4); + i; i = (UCHAR*)USBParseDescriptorWrapper(cfg,len,i+i->bLength,36,4)) +{ + ... // pointer only returned +} // if still in bounds +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker plugs a crafted USB/UVC device. +2. PnP manager loads usbvideo.sys and issues + IRP_MN_START_DEVICE -> SelectDeviceConfiguration(). +3. Driver parses configuration via CountTopologyComponents(), + GetColorDescriptor(), GetEndpointDescriptor(), etc. +4. Malformed descriptor with short/zero bLength causes pointer past end + of DescriptorBuffer. +5. Subsequent reads disclose neighbouring kernel memory to user space – + e.g. through topology IOCTLs or Ks property queries. + + +Attack Vector +-------------------------------------------------------------------- +Local physical. An attacker needs to connect a specially crafted USB +camera or adapter that advertises invalid (short) video descriptors. +No elevated privileges are required once the device is inserted. + + +Patch Description +-------------------------------------------------------------------- +• Introduced USBParseDescriptorWrapper() performing strict length-range + verification before returning a descriptor pointer. +• Replaced direct pointer arithmetic and USBD_ParseDescriptors calls + with the safe wrapper. +• Added explicit size checks such as + if (desc->bLength < expectedMin) return STATUS_INVALID_PARAMETER; +• Added Feature_* gating so the new code path can be enabled broadly + (registry/feature switch). +• Added early returns with STATUS_INVALID_PARAMETER for malformed + descriptors instead of continuing parsing. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a malicious USB device could force the kernel driver +to read beyond allocated memory, allowing disclosure of kernel-resident +information to user-mode processes or the attacker (information +leakage). Although no write occurs, leaked pointers or pool contents +can be used to bypass KASLR or as a primitive for further exploitation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new wrapper enforces both starting offset and descriptor length to +stay inside the advertised Configuration Descriptor size, preventing the +out-of-bounds read demonstrated by the PoC. All known code paths that +previously advanced blindly were updated. No residual unchecked pointer +arithmetic was observed in the supplied diff, indicating the fix is +comprehensive; nevertheless, other unmodified parsing helpers should be +reviewed to rule out similar issues. + diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24056_tapisrv.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24056_tapisrv.dll.txt new file mode 100644 index 0000000..2da1f77 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24056_tapisrv.dll.txt @@ -0,0 +1,128 @@ +{'patch_store_uid': '0e82ece3-aaa2-4a39-8876-183ebba7b2c7', 'cve': 'CVE-2025-24056', 'file': 'tapisrv.dll', 'date': 1751866746.978345, 'kb': 'KB5053598', 'confidence': 0.27, 'change_count': 4} +-------------------------------------------------------------------- +CVE-2025-24056 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Telephony Server (tapisrv.dll) – registry helper code that +builds in-memory priority-list strings: + • GetPriorityList() + • GetMediaModesPriorityLists() + • LSetAppPriority() +These functions are reachable through RPC calls exposed by the +Telephony service (TAPI) running as NT AUTHORITY\SYSTEM. + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write (CWE-122) caused by +missing length validation and unsigned arithmetic underflow while +building wide-char strings. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. GetPriorityList() + • Queries a registry value size into DWORD cbData. + • Allocates cbData+2 bytes then copies cbData bytes at offset +2. + • The code writes a terminating NULL with + *(WORD *)&buf[ 2*((cbData-2)>>1 ) + 2 ] = 0; + • When cbData is 0 or 1 the subtraction underflows (unsigned), + resulting in a write far beyond the end of the heap block. + • No guard existed when the internal feature flag + Feature_1390216506__private_IsEnabledDeviceUsageNoInline() was + disabled (default on down-level builds). + +2. GetMediaModesPriorityLists() contained the same pattern while + looping over registry values. The identical underflow produces an + out-of-bounds write for every malformed value. + +3. LSetAppPriority() later concatenates / removes those priority + strings. Although not the primary corruptor, its logic assumes the + earlier buffers are valid; once the heap is already corrupted the + additional memmove/StringCb* operations can be steered into further + memory corruption and eventual code execution. + +Input parameters and structures involved + HKEY – caller-controlled registry key + lpValueName – value name enumerated by the service + cbData (DWORD) – size returned by RegQueryValueExW + BYTE *buf – heap buffer of size cbData+2 allocated from + ghTapisrvHeap (Process heap of the service) + +Failure sequence + cbData == 0/1 (attacker-supplied) + cbData-2 => 0xFFFFFFFE/-1 (unsigned underflow) + index_calc => huge offset + 2-byte NULL write past heap block -> heap metadata clobbered; + subsequent HeapAlloc/Free triggers controlled corruption. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (RegQueryValueExW(..., buf+2, &cbData)) { ... } +* (WORD *)&buf[ 2 * (((unsigned long long)cbData - 2) >> 1) + 2 ] = 0; +``` +```c +// after +if (RegQueryValueExW(..., buf+2, &cbData) || cbData < 2) +{ + Common::GlobalHeap::Free(buf); + return; +} +* (WORD *)&buf[ 2 * (((unsigned long long)cbData - 2) >> 1) + 2 ] = 0; +``` +The single extra test `cbData < 2` removes the underflow condition. + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client RPC -> lineSetAppPriority (winsrv interface) + -> LSetAppPriority() + -> GetMediaModesPriorityLists() / GetPriorityList() + -> RegQueryValueExW() returns attacker-controlled size + -> under-validated cbData triggers heap overflow. + + +Attack Vector +-------------------------------------------------------------------- +An unauthenticated network attacker can invoke TAPI remote APIs to +force the Telephony service to open an attacker-writable registry key +(e.g. HKCU when impersonating the caller or a per-user mapping) and to +read a value whose data length is 0 or 1. When the service processes +this value the out-of-bounds write corrupts the heap, allowing crafted +heap Feng-shui to gain code execution in the SYSTEM context. + + +Patch Description +-------------------------------------------------------------------- +• Removed feature-flag dependent branches – size checks are now + unconditional. +• Added explicit `cbData < 2` test after the second RegQueryValueExW + in both GetPriorityList() and GetMediaModesPriorityLists(); failure + aborts processing and frees the buffer. +• Reworked LSetAppPriority() to use the hardened helpers and removed + obsolete arithmetic guarded by the same feature flag. +• No structural changes to the heap layout; only validation logic was + added. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix any user able to influence the registry value size +could achieve a 2-byte write at an attacker-controlled heap offset. +By grooming the Telephony service heap this primitive is sufficient +for reliable remote code execution with SYSTEM privileges. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added `cbData < 2` guard blocks the unsigned underflow, ensuring +that the terminating NULL is written **inside** the allocated +(cbData+2) buffer. Re-testing with cbData == 0/1 now triggers the free +path and no out-of-bounds access occurs. Static review shows no +remaining code paths that write beyond the allocation based solely on +cbData; therefore the patch fully addresses the identified issue. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24059_clfs.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24059_clfs.sys.txt new file mode 100644 index 0000000..802a9d1 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24059_clfs.sys.txt @@ -0,0 +1,114 @@ +{'confidence': 0.23, 'file': 'clfs.sys', 'change_count': 2, 'kb': 'KB5053598', 'cve': 'CVE-2025-24059', 'patch_store_uid': 'eac50653-b423-456c-aaff-ceb853a1fd0d', 'date': 1751831414.796584} +-------------------------------------------------------------------- +CVE-2025-24059 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Common Log File System (CLFS) kernel driver, clfs.sys. The +affected routines are CClfsLogFcbPhysical::WriteRestart() and +CClfsLogFcbPhysical::PurgeCacheSection(). + +Vulnerability Class +-------------------------------------------------------------------- +Integer-overflow/incorrect numeric conversion leading to out-of-bounds +cache purge and potential kernel memory corruption (CWE-681, CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CLFS identifies log positions with a 64-bit CLS_LSN structure: + bits 63..32 – cidContainer (logical container number) + bits 31..9 – idxRecord (record number within container) + bits 8..0 – BlockOffset (byte offset within physical sector) + +Two weaknesses existed: +1. WriteRestart() accepted an attacker-controlled restart-LSN (plsn) + without verifying that BlockOffset was inside the physical sector + size held in this->cbPhysicalSector ( *(this+0x2B8) ). A value >= + sector size let idxRecord/BlockOffset pair map outside the current + container when later translated to a byte offset. +2. PurgeCacheSection() converted two user supplied LSNs to byte + offsets using 32-bit arithmetic: + v17 = ((idx & 0xFFFFFE00) + sector*cid) - base; + The result was stored in signed 64-bit and aligned to 4 KiB. A + crafted cidContainer or idxRecord caused the subtraction to wrap + and produce a negative (very large) value. The value was then + handed to CcPurgeCacheSection(), forcing the cache manager to purge + pages far outside the valid file mapping – effectively an + arbitrary in-kernel memory invalidation primitive. + + Because the same unchecked LSN first reaches WriteRestart() from + user FSCTLs (e.g. CLFS_IOCTL_WRITE_RESTART_AREA) and is cached in + memory, the attacker can force CLFS to operate on addresses beyond + the intended section object, leading to EoP via memory corruption. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v17 = ((unsigned int)ullOffset & 0xFFFFFE00) + + *((_QWORD *)this + 87) * HIDWORD(v21) - + ((*v15 & 0xFFFFFE00) + *((_QWORD *)this + 87) * v16); +FileOffset = v17 & 0xFFFFFFFFFFFFF000ULL; // may wrap + +// after – start/end LSNs are realigned, overflow paths rejected +if ((plsn->idxRecord & 0x1FF) != 0 || + ClfsLsnBlockOffset(plsn) >= this->cbPhysicalSector) + return STATUS_INVALID_PARAMETER; +... +FileOffset.QuadPart = CClfsLogFcbPhysical::LsnToCacheOffset(...) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +user-mode -> NtFsControlFile( CLFS_IOCTL_WRITE_RESTART ) + -> clfs.sys -> CClfsBaseFile::WriteRestart() + -> CClfsLogFcbPhysical::WriteRestart() + * accepts attacker LSN, no BlockOffset check (old) + -> CClfsLogFcbPhysical::PurgeCacheSection() + * converts LSN to byte offset with 32-bit math + * calls CcPurgeCacheSection() with wrapped offset + * cache manager purges arbitrary kernel pages + +Attack Vector +-------------------------------------------------------------------- +Any local user able to open a CLFS log file (CreateFile on a log or +TXF transaction file) can send the restart-area FSCTL with a crafted +CLS_LSN whose BlockOffset >= sector-size and/or oversized +cidContainer. No administrator privileges are required. Successful +exploitation yields arbitrary kernel memory invalidation and therefore +local privilege escalation to SYSTEM. + +Patch Description +-------------------------------------------------------------------- +1. WriteRestart(): + – Added explicit validation: + if (BlockOffset >= this->cbPhysicalSector) + return STATUS_INVALID_PARAMETER; + – Realigned earlier arithmetic to 64-bit safe helpers. + +2. PurgeCacheSection(): + – Introduced manual clearing of BlockOffset bits (idx&0x1FF) before + arithmetic. + – Rewrote offset computation using 64-bit helpers + LsnToCacheOffset() and defensive alignment. + – Added extra bounds checks against first-record pointer held in the + FCB ( *(this+1368) ). + – Early exit paths return success without touching cache when the + requested range is invalid. + +Security Impact +-------------------------------------------------------------------- +Before the patch a crafted CLS_LSN could push CLFS into computing a +negative or over-large CACHE_OFFSET that is forwarded to the cache +manager, corrupting kernel memory and granting the attacker arbitrary +code execution with kernel privileges (Elevation of Privilege). + +Fix Effectiveness +-------------------------------------------------------------------- +The added BlockOffset-vs-SectorSize check completely blocks the +original path. New 64-bit calculations remove wrapping behaviour and +all purge offsets are clamped to valid ranges. No alternate path to +CcPurgeCacheSection() was found in the diff, suggesting the fix is +comprehensive, though other CLFS routines that accept raw CLS_LSNs +should be audited for identical assumptions. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24061_urlmon.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24061_urlmon.dll.txt new file mode 100644 index 0000000..de72224 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24061_urlmon.dll.txt @@ -0,0 +1,100 @@ +{'confidence': 0.22, 'cve': 'CVE-2025-24061', 'patch_store_uid': '83b7efe5-5e7b-4cba-a708-89e8ea257391', 'file': 'urlmon.dll', 'change_count': 10, 'date': 1751865234.205103, 'kb': 'KB5053598'} +-------------------------------------------------------------------- +CVE-2025-24061 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +urlmon.dll – function GetServerShareFromIUriPriv(), used by the URL +Security Manager (CSecurityManager) when it computes the security ID +and zone for a supplied IUri / file: URL. + +Vulnerability Class +-------------------------------------------------------------------- +Logic error / security-feature bypass (CWE-693 – Protection Mechanism +Failure). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GetServerShareFromIUriPriv() tries to decide whether the path part of a +file: URI refers to a local drive ("C:") or to a network resource. It +first skips leading back-slashes, then calls IsDrive() to test the next +segment. If IsDrive() returns TRUE the code assumes a local drive, +queries the drive type, and – if the drive is not a mapped network +drive – treats the file as local (Local Machine zone, no MOTW). + +IsDrive() only looks for the pattern "<char>:" and is unaware of IPv6 +literals inside UNC names, e.g. "\\[2001:db8::1]\share". After the +leading "\\" are skipped, the first segment is "[2001:db8::1]"; the +colon found inside that segment makes IsDrive() return TRUE even though +this is *not* a drive letter. Consequently the function sets + + *v57 = 1 ; marks path as drive + *pszDest = <type> ; drive type + v51 = 1 ; early-exit flag + +and finally hands back a security ID that corresponds to a local path. +All downstream checks therefore believe the resource is on the local +machine and do not honour Mark-of-the-Web, sandboxing, etc. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +for ( i = pbstr[1]; *i == '\\' || *i == '/'; ++i ) + ; +if ( (unsigned int)IsDrive(i) ) // mis-detects "[IPv6]" as drive +{ + ... +} + +// after +for ( i = pbstr[1]; *i == '\\' || *i == '/'; ++i ) + ; +if ( IsDriveAndNotIPv6(i) ) // extra IPv6 guard +{ + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Victim opens a file/URL that eventually reaches: + CSecurityManager::GetSecurityId* -> GetServerShareFromIUriPriv() + 1. Function skips leading "\\". + 2. IsDrive() wrongly accepts first segment of an IPv6 UNC as "C:". + 3. Function flags path as local drive and returns security ID. + 4. Caller assigns Local Machine / Intranet zone, bypassing MOTW. + +Attack Vector +-------------------------------------------------------------------- +Any local context that causes Windows to evaluate a file: URL or UNC +path controlled by an attacker (e.g. a shortcut, HTML, or script) can +exploit the bug. A path such as: + file:////[2001:db8::dead:beef]/share/evil.docx +is interpreted as a local drive, allowing active content to run without +Mark-of-the-Web restrictions. + +Patch Description +-------------------------------------------------------------------- +The patch replaces the bare IsDrive() call with a new helper +IsDriveAndNotIPv6(). The new routine returns TRUE only when the segment +is a single-letter drive designator and *not* an IPv6 literal. No other +code changes were needed. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could store content on a remote share that +is addressed through an IPv6 literal and have Windows treat that content +as coming from the local machine zone. This bypasses the Mark-of-the- +Web flag and all mitigations that rely on it (Protected View, Office +mode, Smart App Control, etc.). Result: arbitrary code execution with +user privileges by opening a crafted document or archive. + +Fix Effectiveness +-------------------------------------------------------------------- +By introducing an explicit IPv6 check the direct mis-classification path +is closed. No alternative entry in GetServerShareFromIUriPriv() keeps +the old IsDrive() behaviour, so the fix appears complete for this code +path. A defence-in-depth review of other callers of IsDrive() is still +recommended. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24066_ks.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24066_ks.sys.txt new file mode 100644 index 0000000..b6e2267 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24066_ks.sys.txt @@ -0,0 +1,135 @@ +{'file': 'ks.sys', 'cve': 'CVE-2025-24066', 'confidence': 0.26, 'kb': 'KB5053598', 'patch_store_uid': '885e1d78-ef78-499a-8de2-95ab2f6828b6', 'date': 1751831352.1044295, 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-24066 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kernel Streaming driver (ks.sys). The vulnerable logic +resides in the streaming-IRP probing path and the MDL-cache helper +routines that manipulate caller–supplied KSSTREAM_HEADER arrays: + • KsProbeStreamIrp() + • CKsMdlcache::MdlCacheHandleThunkBufferIrp() + • CKsQueue::TransferKsIrp() + • CKsQueue::PutAvailableFrameHeader() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / insufficient input validation (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +A user-mode client submits a Read / Write (stream) IRP that contains +one or more KSSTREAM_HEADER structures. Each header carries: + Size @ +0x00 (dword) + DataUsed @ +0x20 (dword) [array index 9] + Data @ +0x28 (qword) [index 10] + OptionsFlags @ +0x30 (dword) [index 12] + MetaDataPtr @ +0x88 (qword) [index 17] + MetaDataSize @ +0x80 (dword) [index 32] + +Before the patch KsProbeStreamIrp() and +MdlCacheHandleThunkBufferIrp() performed only superficial checks. +If OptionsFlags contained KSSTREAM_HEADER_OPTIONSF_VARIABLESIZE +(0x1000) the code still accepted headers whose + • MetaDataPtr == NULL, **or** + • MetaDataSize == 0 + +Yet the same functions later attempted to call IoAllocateMdl() for +MetaDataPtr/MetaDataSize and to memmove() the meta data into a fixed +0x80-byte buffer inside a 0xB0-byte frame header object. When a +malicious caller supplied VariableSize headers with a large Size +field (>0x80) but zero MetaData, the driver: + 1. Allocated an MDL of length 0 or 0x1000. + 2. Copied the attacker-controlled header (Size bytes) into the small + 0x80-byte area (CKsQueue::TransferKsIrp), overflowing heap memory. + 3. The overwrite occurs in the non-paged pool under the + ‘PcFhKd’ / ‘KsMcS’ tags, granting arbitrary kernel write. + +The original code path (excerpt): +```c +/* before */ +if ((UserBuf[12] & 0x1000) && v8 > 0x80 && + !IoAllocateMdl(UserBuf[17], UserBuf[32], 1, 1, Irp)) { + status = STATUS_INSUFFICIENT_RESOURCES; +} +``` +No NULL / zero-length checks were present. + +Patch adds explicit validation and early bail-out: +```c +/* after */ +if ((hdr->Options & 0x1000) && hdr->Size > 0x80) { + if (!hdr->MetaDataPtr) FAIL; + if (!hdr->MetaDataSize) FAIL; + IoAllocateMdl(...); +} +``` +Equally, KsProbeStreamIrp() now rejects headers <0xA0 when the +VARIABLESIZE flag is set and refuses NULL/zero meta data pointers. +Pointers inside released frame headers are also cleared to prevent +use-after-free. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// 1. Missing validation (before) +if ((UserBuffer[12] & 0x1000) != 0 && v8 > 0x80 && + !IoAllocateMdl(*((PVOID*)UserBuffer+17), UserBuffer[32], ...)) + status = STATUS_INSUFFICIENT_RESOURCES; + +// 2. Fixed (after) +if ((hdr->Options & 0x1000) && hdr->Size > 0x80) { + if (!hdr->MetaDataPtr || !hdr->MetaDataSize) { + RtlLogUnexpectedCodepath(...); + return STATUS_INVALID_PARAMETER; + } + IoAllocateMdl(hdr->MetaDataPtr, hdr->MetaDataSize, ...); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens a KS pin (e.g. camera) and sends a READ/WRITE + stream IRP with crafted KSSTREAM_HEADER chain. +2. KsProbeStreamIrp() copies the headers into kernel pool without + validating VariableSize meta data fields. +3. CKsMdlcache::MdlCacheHandleThunkBufferIrp() allocates zero-length + MDL, leaving the subsequent copy unchecked. +4. CKsQueue::TransferKsIrp() builds a 0xB0-byte frame header and + memmoves Size bytes (>0x80) into the 0x80-byte internal buffer, + overflowing heap memory. +5. Overwrite of adjacent pool objects yields kernel code execution + when attacker-controlled function pointers / list entries are hit. + +Attack Vector +-------------------------------------------------------------------- +Any local, low-privileged user that can open a Kernel Streaming +endpoint (e.g. /Device/Camera, audio pin, etc.) can send the +malformed IRP. No special privileges are required beyond device +access. + +Patch Description +-------------------------------------------------------------------- +• Added strict NULL / zero-length checks for VARIABLESIZE (0x1000) + headers in KsProbeStreamIrp(), MdlCacheHandleThunkBufferIrp(), and + TransferKsIrp(). +• Rejects mis-aligned or undersized headers (<0xA0 when 0x1000 set). +• Logs and returns STATUS_INVALID_PARAMETER on bad input instead of + continuing. +• Clears dangling pointers when frame headers are recycled. +• Removes feature-flag short-cuts; validation is now unconditional. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could trigger a heap-based buffer +overflow in non-paged pool, leading to arbitrary kernel memory write +and reliable local elevation of privilege (ring-0 execution) or +system crash. + +Fix Effectiveness +-------------------------------------------------------------------- +The new guards exit early before any MDL allocation or memmove when +meta-data pointer/size fields are invalid, and object fields are +sanitised on recycle. No unchecked path that copies >0x80 bytes into +0x80-byte buffers remains, so the original overflow is removed. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24067_ks.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24067_ks.sys.txt new file mode 100644 index 0000000..9a9414a --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24067_ks.sys.txt @@ -0,0 +1,137 @@ +{'change_count': 8, 'patch_store_uid': '885e1d78-ef78-499a-8de2-95ab2f6828b6', 'file': 'ks.sys', 'kb': 'KB5053598', 'date': 1751831437.6295638, 'cve': 'CVE-2025-24067', 'confidence': 0.23} +-------------------------------------------------------------------- +CVE-2025-24067 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Kernel-Streaming driver (ks.sys). Affected paths +include KsCreateDefaultAllocatorEx, KsProbeStreamIrp, +CKsMdlcache::MdlCacheHandleThunkBufferIrp and +CKsMdlcache::MdlCacheProcessPostProbeIrp – the code that parses +user-supplied KSSTREAM_HEADER structures, creates look-aside frame +allocators and builds MDLs for streaming I/O. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / improper parameter validation (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. User code sends IRP_MJ_READ/WRITE or Ioctl 0x2F0007 to a KS pin. +2. The IRP contains one or more KSSTREAM_HEADER structures that may + carry the flag KSSTREAM_HEADER_OPTIONSF_BUFFERED (0x1000) and an + optional secondary buffer pointer/length located at offsets 0x88 + and 0x80 of the header. +3. Before the patch the helpers below trusted these fields when + allocating kernel objects: + • KsProbeStreamIrp – allocates MDLs directly from + Header->Data and Header->FrameExtent without verifying that + Data2/Size2 are non-NULL/non-zero when OPTIONSF_BUFFERED is + set. + • CKsMdlcache::MdlCacheHandleThunkBufferIrp – repeats the same + logic while building a private MDL cache. +4. If an attacker sets OPTIONSF_BUFFERED, supplies FrameExtent > 0x80 + but leaves Data2 or Size2 NULL/0, the code reaches + IoAllocateMdl(NULL, 0, …) + which returns an MDL whose mapped buffer overlaps the driver’s + own NonPagedPool allocation. Subsequent data moves by + KspCopyFrame overflow the original 0xB0-byte frame header buffer + and corrupt adjacent pool memory – a classic heap overflow. +5. A second entry point lay in KsCreateDefaultAllocatorEx. When the + internal *Feature_H2E_WPA3SAE* switch was disabled (default on + desktop SKUs) the function accepted any FrameSize. A non power-of + two value is passed as the Size parameter to + ExInitialize[NP]PagedLookasideList. The look-aside code rounds + the allocation down, so later writes that assume the full + requested size run past the end of each block, again corrupting + pool memory. +6. Because all paths run in kernel mode, successful exploitation lets + a normal user execute arbitrary code with SYSTEM privileges. + +Key affected fields/structures + KSSTREAM_HEADER + 0x00 – Size + 0x20 – FrameExtent + 0x24 – DataUsed + 0x40 – Options (bit 12 = 0x1000, BUFFERED) + 0x80 – Data2Length + 0x88 – Data2Pointer + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – KsProbeStreamIrp +if ((hdr->Options & 0x1000) && FrameExtent>0x80 && + !IoAllocateMdl(hdr->Data2, hdr->Data2Length, ...)) + status = STATUS_INSUFFICIENT_RESOURCES; // no pointer checks + +// after – extra validation added +if ((hdr->Options & 0x1000) && FrameExtent>0x80) { + if (!hdr->Data2 || !hdr->Data2Length) { + RtlLogUnexpectedCodepath(...); + return STATUS_INVALID_PARAMETER; + } + IoAllocateMdl(...); +} + +// before – KsCreateDefaultAllocatorEx +if (!Feature_H2E_WPA3SAE_IsEnabled() || IsUserAllocator || + power_of_two(FrameSize)) // validation could be bypassed + ExInitializeNPagedLookasideList(..., FrameSize, ...); + +// after – feature gate removed, power-of-two enforced +if (!IsUserAllocator && !power_of_two(FrameSize)) + return STATUS_INVALID_PARAMETER; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User mode + Open pin -> DeviceIoControl(KSSTREAMING_IOCTL_CREATE_DEFAULT_ALLOC) + -> send IRP_MJ_WRITE with crafted KSSTREAM_HEADER +Driver + KsCreateDefaultAllocatorEx accepts non-power-of-two FrameSize + -> look-aside list blocks smaller than header size + KsProbeStreamIrp / MdlCacheHandleThunkBufferIrp allocate MDL for + NULL Data2 pointer -> overlapping buffer + -> KspCopyFrame writes past 0xB0-byte frame header + -> pool corruption -> EoP. + +Attack Vector +-------------------------------------------------------------------- +Any local user that can open a KS pin (e.g. via DirectShow, Media +Foundation or win32ks APIs) can submit the malicious IRPs. No +additional privileges are required beyond normal device access. + +Patch Description +-------------------------------------------------------------------- +• Removed obsolete feature-flag gate; validation is now unconditional. +• KsCreateDefaultAllocatorEx now rejects FrameSize values that are not + a power-of-two unless the caller supplies its own allocate routine. +• KsProbeStreamIrp and MdlCacheHandleThunkBufferIrp now + – verify Data2Pointer and Data2Length are both non-zero when + OPTIONSF_BUFFERED is set, + – log with RtlLogUnexpectedCodepath and fail with + STATUS_INVALID_PARAMETER instead of continuing. +• Added clean-up code that zeroes Irp->MdlAddress on failure to avoid + use-after-free. +• Replaced several direct assignments with safer helpers and corrected + pointer arithmetic. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a crafted streaming IRP could corrupt kernel heap +memory, giving an attacker arbitrary read/write primitives and +allowing local privilege escalation to SYSTEM (ring-0). The exploit +triggers from user mode without needing code execution in the device +context. + +Fix Effectiveness +-------------------------------------------------------------------- +The new size and pointer checks block the malformed headers before any +allocation occurs; IoAllocateMdl is never reached with an invalid +pointer and look-aside lists are initialised with a guaranteed safe +block size. Combined with the unconditional enforcement (feature flag +removed) the vulnerable paths are no longer reachable, and no bypasses +are apparent in the patched logic. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24071_shell32.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24071_shell32.dll.txt new file mode 100644 index 0000000..06af364 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24071_shell32.dll.txt @@ -0,0 +1,122 @@ +{'kb': 'KB5053598', 'confidence': 0.19, 'date': 1751831492.943622, 'change_count': 424, 'file': 'shell32.dll', 'cve': 'CVE-2025-24071', 'patch_store_uid': 'fcefe295-6bdc-4231-8a6b-8a69f8045759'} +-------------------------------------------------------------------- +CVE-2025-24071 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows shell32.dll – helper routine +std::_Traits_find_last_of<std::char_traits<ushort>,1>() +(used by File Explorer path-handling code) + +Vulnerability Class +-------------------------------------------------------------------- +Logic flaw / incorrect function body (symbol resolution error) +leading to incorrect path parsing; results in spoofing / information +exposure (CWE-200). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +In the pre-patch binary the symbol + + std::_Traits_find_last_of<std::char_traits<ushort>,1> + +points to a completely unrelated lambda-destructor generated from +CPinnedList::ApplyPrependDefaultTaskbarLayout. That stub does nothing +except call std::wstring::~wstring on + + (parameter2 + 0x70) // 0x70 (112) bytes offset + +and returns its result. The routine clearly does **not** implement the +expected find_last_of() semantics (returning the last occurrence of +one of the supplied separator characters in a wide string). Every call +site that expects an index (ssize_t) instead receives whatever value is +returned by the destructor – in practice usually –1 or a small heap +pointer. Down-stream code that relies on the index to split or +canonicalise a path therefore sees corrupt data. + +File Explorer uses this helper to locate the last '\\' or '/' in a +UNICODE path. With the bogus implementation the search fails whenever +separator characters are involved, so the caller treats the **entire** +input as a file name instead of <dir>\<file>. A crafted remote share +name such as + + \\\\ATTACKER\public\..\really.docx + +can therefore be mis-presented as a single "docx" item while internally +still resolving to an arbitrary ancestor directory. The GUI shows the +spoofed value, but the underlying I/O APIs open the true location; this +constitutes a spoofing / information disclosure scenario. + +The bug is entirely caused by a build/link fault: the template +specialisation with integral constant parameter ==1 never received its +proper body, leaving the compiler-generated destructor in its place. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – wrong body, just a lambda destructor +__int64 __fastcall CPinnedList::ApplyPrependDefaultTaskbarLayout_::_1_::dtor_3( + __int64 a1, __int64 a2) +{ + return std::wstring::~wstring(a2 + 112); +} + +// after patch – real implementation (excerpt) +__int64 __fastcall std::_Traits_find_last_of<...>( + _WORD *str, __int64 len, __int64 pos, + const wchar_t *search, __int64 searchLen) +{ + char mask[256] = {0}; + if (!searchLen || !len) return -1; + for (const wchar_t *p = L"\\/"; p != search + searchLen; ++p) { + if (*p >= 0x100) // non-ASCII => fall back + return std::_Traits_find_last_of<...,0>(...); + mask[(unsigned char)*p] = 1; + } + ... // walk backwards and return idx +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. File Explorer constructs a UNICODE path string. +2. Path canonicalisation helper calls + std::_Traits_find_last_of<ushort,1>() to locate the last separator. +3. Pre-patch, the call lands in the stray destructor. +4. A wrong value is returned -> caller uses bogus slice indices. +5. GUI renders spoofed file name; underlying file object resolves to a +different location. + +Attack Vector +-------------------------------------------------------------------- +An attacker controls a path presented to the victim (e.g. via a network +share, ZIP, or shortcut). By embedding special directory components +and relying on the missing find_last_of() logic, the attacker can cause +File Explorer to display a misleading file/folder name, leading the user +to open or copy unintended content. + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the destructor stub with a full, bounds-checked ASCII +implementation of std::_Traits_find_last_of<ushort,1>(). Key safety +measures: +• validates both string length (a2) and search-set length (a5) +• builds a 256-byte mask only for ASCII values; if a wchar_t >255 occurs + it transparently defers to the generic template (parameter 0) +• walks the input string from the end to the beginning and returns a + correct 0-based index, -1 on failure. + +Security Impact +-------------------------------------------------------------------- +Correcting the routine restores accurate path delimiter detection. The +GUI can no longer be tricked into showing a falsified name, closing the +spoofing / info-leak hole. No memory corruption remains reachable from +untrusted input. + +Fix Effectiveness +-------------------------------------------------------------------- +Because the faulty symbol was entirely replaced, all call sites now +receive valid indices. The added high-value wchar_t check prevents any +future mask overrun. Without other code paths mapping to the old stub, +this patch fully mitigates the identified vulnerability. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24071_windows.ui.fileexplorer.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24071_windows.ui.fileexplorer.dll.txt new file mode 100644 index 0000000..e11e2ff --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24071_windows.ui.fileexplorer.dll.txt @@ -0,0 +1,117 @@ +{'kb': 'KB5053598', 'confidence': 0.36, 'patch_store_uid': '14604a4c-0db0-47c1-b611-b2fcc654f933', 'cve': 'CVE-2025-24071', 'change_count': 189, 'date': 1751831488.5761058, 'file': 'windows.ui.fileexplorer.dll'} +-------------------------------------------------------------------- +CVE-2025-24071 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.ui.fileexplorer.dll (File Explorer / Windows.UI.FileExplorer +utility helpers) + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure / Spoofing caused by improper object type +validation (CWE-200) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The DLL exports a helper called +GetShellItemFromNavigationTarget(IUnknown *,IShellItem **). In the +vulnerable build the export was incorrectly resolved to the destructor +of an unrelated template class + ~FileExplorerFolderViewController(...) +which merely executed + return BaseClassDtor(a1 + 0x50); +This surrogate implementation blindly treats the first parameter as a +valid FileExplorerFolderViewController object and dereferences it with +an 0x50-byte positive offset before invoking a virtual destructor. +No interface check, no reference counting, and no HRESULT are +returned. Supplying any other IUnknown pointer therefore results in an +out-of-bounds object access inside Explorer’s process. Information +contained in the adjacent memory (including path strings and other UI +state) can be read back or mis-interpreted, enabling spoofing of the +address-bar or detail panes. + +The patched build restores a correct implementation. The routine now + +1. Calls IUnknown_QueryService to translate the inbound IUnknown into + a folder navigation service (GUID + 2ce15729-376f-4372-ad30-4148a6326ee8). +2. Uses that service’s virtual GetObject call (vtable +0x58) to obtain + an IUnknown representing the navigation target. +3. Invokes SHGetItemFromObject to securely retrieve the corresponding + IShellItem pointer for the caller. +4. Propagates any failure as a proper HRESULT and releases every COM + reference acquired. + +All pointer use is now guarded by COM-style interface discovery and the +function never dereferences attacker-controlled memory directly. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* vulnerable build */ +__int64 __fastcall ExportedSymbol(__int64 a1) +{ + return BaseClassDtor(a1 + 0x50); // unchecked offset into caller +} + +/* fixed build */ +HRESULT __fastcall GetShellItemFromNavigationTarget(IUnknown *unk, + IShellItem **ppsi) +{ + void *srv = NULL; + IUnknown *punk = NULL; + HRESULT hr = IUnknown_QueryService(unk,&IID_SomeSvc,&IID_SomeSvc,&srv); + if (FAILED(hr)) goto Exit; + hr = ((PFN)(*(PVOID *)srv)[11])(srv,&IID_IShellFolder,&punk); + if (FAILED(hr)) goto Exit; + hr = SHGetItemFromObject(punk,&IID_IShellItem,(void**)ppsi); +Exit: + if (punk) punk->Release(); + if (srv) ((IUnknown*)srv)->Release(); + return hr; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Explorer processes a navigation request and passes an IUnknown that + may originate from a network-delivered COM proxy. +2. Explorer calls exported GetShellItemFromNavigationTarget. +3. Vulnerable build immediately executes a destructor on (a1+0x50), + touching arbitrary memory. +4. Memory adjacent to the attacker-controlled buffer is interpreted as + internal data and can be surfaced in UI elements. + +Attack Vector +-------------------------------------------------------------------- +An attacker supplies a malicious navigation target (eg. crafted +shortcut, library, or remote COM object) over the network. When +Explorer invokes the helper export, the wrong implementation operates +on attacker-controlled memory, allowing spoofing of folder paths or +exposure of memory belonging to the Explorer process. + +Patch Description +-------------------------------------------------------------------- +The export was re-implemented. The new code: +* Performs proper service discovery with IUnknown_QueryService +* Obtains the target IShellItem through SHGetItemFromObject +* Returns HRESULTs and records diagnostics with wil::Return_Hr +* Releases all intermediate COM interfaces unconditionally +No unsafe pointer arithmetic remains. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, arbitrary memory could be read or interpreted as +shell data, letting a network attacker spoof File Explorer UI +indicators or leak private information from the process heap. While +no direct code execution is shown, the issue violates process memory +isolation and user trust in displayed paths. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable offset dereference path has been completely removed. +Interface validation, explicit error handling, and reference tracking +eliminate the previously unsafe memory access, fully neutralising the +spoofing / disclosure primitive. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24072_lsasrv.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24072_lsasrv.dll.txt new file mode 100644 index 0000000..e4ee1c0 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24072_lsasrv.dll.txt @@ -0,0 +1,112 @@ +{'file': 'lsasrv.dll', 'confidence': 0.21, 'kb': 'KB5053598', 'patch_store_uid': '312449ae-99b8-496d-a3af-1fc02b542384', 'date': 1751831437.9137857, 'cve': 'CVE-2025-24072', 'change_count': 54} +-------------------------------------------------------------------- +CVE-2025-24072 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Local Security Authority Sub-system Service (lsasrv.dll), +function GetClientString(), invoked by the LPC password-change path +(LpcChangeAccountPassword). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (invalid free of attacker-controlled pointer) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GetClientString() is responsible for copying a UNICODE_STRING that the +client embeds in an LPC message into an LSASS-owned buffer. The +initial implementation performs a structure assignment + + *a2 = *a1; + +a2 (server-side) therefore inherits *all* fields coming from the +untrusted client, including the Buffer pointer. Immediately +afterwards the function performs parameter validation: + + if (a2->Length > 0xFFFC) + return STATUS_INVALID_PARAMETER; + +If this length check fails GetClientString() returns early. The +callers (e.g. LpcChangeAccountPassword) run generic clean-up code that +unconditionally frees a2->Buffer through LsapSubProv_FreeRoutine(). +Because a2->Buffer still contains the user supplied address, LSASS +attempts to free memory it never allocated. Depending on the supplied +pointer this leads to: + + * freeing an arbitrary kernel heap block twice (UAF), or + * freeing an address that is under attacker control, enabling + arbitrary pointer overwrites in the heap allocator. + +Any subsequent heap allocation in LSASS occurs in a memory region now +controlled or previously freed by the attacker, enabling elevation of +privilege in the highly-privileged LSASS process. + +Important data structures and parameters: + UNICODE_STRING {USHORT Length; USHORT MaximumLength; PWSTR Buffer} + a1 – client-supplied structure inside LPC message + a2 – server-side copy that the caller later frees + Buffer pointer – fully attacker-controlled prior to patch + +The fault is triggered only when the length check fails (or any later +error that exits before the server overwrites Buffer with a freshly +allocated pointer), so reliability is very high. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable fragment (before patch) +*a2 = *a1; // copies attacker buffer ptr +... +if (a2->Length > 0xFFFCu) // validation fails + return STATUS_INVALID_PARAMETER; // caller will free a2->Buffer +``` +```c +// fixed fragment (after patch) +if (FeatureEnabled) // always true after patch rollout + a2->Length = a1->Length; // copy only scalar field +else + *a2 = *a1; // legacy path (disabled) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends LPC request to LSASS via LpcChangeAccountPassword. +2. Embedded UNICODE_STRING contains + Length = 0xFFFE (or any value > 0xFFFC), + Buffer = pointer chosen by attacker. +3. LpcChangeAccountPassword calls GetClientString(). +4. GetClientString() structure-copies Buffer, fails length check, + returns STATUS_INVALID_PARAMETER. +5. Caller executes error-handling path and frees a2->Buffer. +6. LSASS frees attacker pointer, corrupting the heap -> code exec. + +Attack Vector +-------------------------------------------------------------------- +Requires a local authenticated session that can call password-related +LSA APIs (e.g. through LSA RPC). No special privileges are needed +beyond the ability to invoke LpcChangeAccountPassword. + +Patch Description +-------------------------------------------------------------------- +The patch stops copying the client-supplied Buffer pointer into the +server structure. When the corresponding feature flag is enabled the +code now copies only the Length field, leaving Buffer NULL until the +server allocates its own memory. All subsequent frees therefore act +only on trusted allocations. + +Security Impact +-------------------------------------------------------------------- +Successful exploitation yields arbitrary heap free write within LSASS +leading to elevation of privilege to SYSTEM. Because LSASS runs with +high integrity and extensive privileges, full local compromise is +achieved. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code path ensures that a2->Buffer is never attacker-derived +when an error path executes. Provided the feature flag remains +permanently enabled, the UAF is eliminated. The legacy path (*a2 = +*a1) still exists behind the flag; if that flag is ever disabled the +original vulnerability would resurface. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24984_ntfs.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24984_ntfs.sys.txt new file mode 100644 index 0000000..03262d0 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24984_ntfs.sys.txt @@ -0,0 +1,99 @@ +{'patch_store_uid': 'e7ca9e07-1f84-451f-a10b-8a79bb74c0cb', 'cve': 'CVE-2025-24984', 'confidence': 0.21, 'date': 1751831448.490404, 'change_count': 17, 'file': 'ntfs.sys', 'kb': 'KB5053598'} +-------------------------------------------------------------------- +CVE-2025-24984 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NTFS kernel driver (ntfs.sys) – ETW diagnostic logging in +NtfsAbortTransaction, NtfsChangeAttributeValue and DoAction. + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure – CWE-532: Insertion of Sensitive Information +into Log File. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Multiple NTFS restart-management paths emit Event-Tracing-for-Windows +(ETW) records whenever the corresponding provider is enabled +(bits 0x2 = Information, bits 0x4 = Verbose). Before the patch the +code referenced the provider variable + + Microsoft_Windows_NtfsLog_94d10f64af0e3ec7c3d991d5bedd15e9EnableBits + +and wrote events such as McTemplateU0pd_*, McTemplateU0ddid_* or +McTemplateU0ppp_* containing: + • Transaction log sequence numbers + • File-record numbers, attribute names + • Bitmap allocation runs, sector counts and other raw on-disk data + +Because provider 94d10f64-… is not marked "secure" in its manifest, any +process able to start an ETW session (or an attacker with physical +access to the disk where the trace was stored) could collect or read +those events and reconstruct sensitive NTFS metadata and, in some +cases, fragments of user data. + +Functions affected (pre-patch): + • NtfsAbortTransaction – rollback path emits restrsup_c458 / _c465 + • NtfsChangeAttributeValue – value/length change path emits + attrsup_c14780, restrsup_c3684, c3716 … + • DoAction – restart-replay path emits restrsup_c4465, c4633 … + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (NtfsAbortTransaction) +if ((Microsoft_Windows_NtfsLog_94d10f64af0e3ec7c3d991d5bedd15e9EnableBits & 4) != 0) + McTemplateU0pd_EtwWriteTransfer(v6, &restrsup_c458); + +// AFTER +if ((Microsoft_Windows_NtfsLog_c83e0889d8d138db51a3b5e3960eb478EnableBits & 4) != 0) + McTemplateU0pd_EtwWriteTransfer(v6, &restrsup_c461); +``` +(The same replacement appears in the other two functions.) +``` +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Any privileged or unprivileged code (locally or via physical access) + enables the old NtfsLog provider and creates an ETW session. +2. Normal NTFS activity (rollback, attribute change, restart replay) + executes the affected functions. +3. Sensitive on-disk structures are packaged into ETW payloads and + written to the session log file. +4. Attacker reads the trace file and extracts file-system information + that should remain private. + +Attack Vector +-------------------------------------------------------------------- +Local / physical. The attacker needs to start or obtain a trace file +containing the insecure provider’s events (e.g., left on disk after a +support capture, crash-repro, or taken from offline media). + +Patch Description +-------------------------------------------------------------------- +All three functions now reference a new provider variable + + Microsoft_Windows_NtfsLog_c83e0889d8d138db51a3b5e3960eb478EnableBits + +and new event descriptors (restrsup_c461, c468, attrsup_c14780 etc.). +The manifest for the new GUID is marked as a secure/kernel provider, +meaning it is disabled by default and its data is emitted only to +sessions that explicitly request SECURITY_EVENT access. No other data +flow is modified. + +Security Impact +-------------------------------------------------------------------- +Before the fix, NTFS could leak internal metadata (and occasionally +user data) into log files readable by lower-privileged users or anyone +with offline access, violating confidentiality. With the secure +provider this information is no longer logged unless an administrator +creates a protected trace session. + +Fix Effectiveness +-------------------------------------------------------------------- +The only change is the provider GUID and associated descriptors; event +payload construction still exists but is gated behind a provider that +ordinary sessions cannot enable. Thus the sensitive data path is no +longer reachable for normal or accidental logging scenarios. No new +attack surface is introduced, and the mitigation is deemed effective. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24987_usbvideo.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24987_usbvideo.sys.txt new file mode 100644 index 0000000..e2c236b --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24987_usbvideo.sys.txt @@ -0,0 +1,109 @@ +{'patch_store_uid': '13c1e87d-a065-4d51-b526-a46f95d9c939', 'confidence': 0.15, 'cve': 'CVE-2025-24987', 'change_count': 27, 'date': 1751865219.4717171, 'kb': 'KB5053598', 'file': 'usbvideo.sys'} +-------------------------------------------------------------------- +CVE-2025-24987 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows USB Video Class driver (usbvideo.sys) – routine +GetEndpointDescriptor(), invoked while parsing USB configuration and +interface-specific descriptors for UVC devices. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read (CWE-125) that can be leveraged for kernel +information disclosure and local Elevation of Privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine walks a caller-supplied descriptor buffer to locate a USB +endpoint descriptor that matches a requested transfer type (argument +`a3`). + +Pre-patch logic used the following pattern: + • `DescriptorBuffer` was typed as `unsigned __int16 *`. Adding an + integer to that pointer scaled by two, so + `DescriptorBuffer + *v9` + advanced twice as far as the 16-bit size field actually specifies. + • The search loop updated the cursor with raw pointer arithmetic: + i = &v12->bLength + v12->bLength + without validating that `bLength` is non-zero or that the resulting + address still lies inside the original buffer. + • The very first instruction inside the loop did + p_bLength = &v12->bLength; + before checking whether `v12` is NULL. When + `USBD_ParseDescriptors()` failed and returned NULL, the code + nonetheless computed a new pointer based on address 0, producing a + pointer that refers to unmapped or unrelated kernel memory. + +By presenting a crafted configuration descriptor in which the final +sub-descriptor advertises a `bLength` of 0 or a length that extends +beyond the end of the overall descriptor set, an attacker can force the +cursor past the buffer’s boundary. Subsequent iterations call +`USBD_ParseDescriptors()` on this out-of-range address, causing the +kernel to read arbitrary memory. Because descriptor parsing occurs in +the context of `usbvideo.sys` during device enumeration, this condition +is reachable by plugging in a malicious USB video device while an +administrator is logged on. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable loop (pre-patch) +for (i = a2; ; i = &v12->bLength + v12->bLength) { + v12 = USBD_ParseDescriptors(DescriptorBuffer, *v9, i, 5); + p_bLength = &v12->bLength; // executed even when v12 == NULL + if (!v12) + break; // OOB pointer already computed + if (v12[1].bLength == a3) { + if (v12 > NextVideoAltSetting) + p_bLength = 0; + break; + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Malicious USB camera is connected. +2. USB stack issues URB to enumerate configuration. +3. usbvideo.sys -> `USBVid_ParseConfigurationDescriptors()` (caller + name simplified) -> `GetEndpointDescriptor()`. +4. Crafted last descriptor claims `bLength = 0` (or oversize). +5. Loop in `GetEndpointDescriptor()` advances past buffer, performs + out-of-bounds read when calling `USBD_ParseDescriptors()` again. +6. Kernel discloses memory or crashes, enabling further exploitation. + +Attack Vector +-------------------------------------------------------------------- +Physical attack with a specially crafted USB Video Class device (or +emulator) that returns a malformed configuration or interface alternate +setting descriptor block when the host requests descriptors. + +Patch Description +-------------------------------------------------------------------- +• Parameter `DescriptorBuffer` retyped to `char *`, eliminating 2-byte + scaling side effects. +• New `StartPosition` argument replaces unsafe in-function cursor math. +• Calls replaced with `USBParseDescriptorWrapper()`, which receives the + total buffer size and a minimum descriptor length (7), ensuring the + returned descriptor is fully contained within the buffer. +• Added NULL checks before every dereference or pointer arithmetic. +• After locating a candidate descriptor the code verifies that the + pointer is not past `NextVideoAltSetting`; otherwise it is nulled. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a local, physical attacker could supply descriptors +that trick usbvideo.sys into reading kernel memory outside the trusted +USB buffer. Information disclosure can bypass KASLR or leak tokens and +may be chained with other bugs for full kernel Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The wrapper-based parsing enforces explicit length checks, and all +pointer arithmetic is now performed only after verifying non-NULL +pointers and buffer boundaries. Combined with the corrected pointer +base type, the OOB read avenue is closed. No alternate paths in the +function bypass the new checks, so the patch appears complete for this +routine. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24988_usbvideo.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24988_usbvideo.sys.txt new file mode 100644 index 0000000..99ecdd4 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24988_usbvideo.sys.txt @@ -0,0 +1,116 @@ +{'kb': 'KB5053598', 'cve': 'CVE-2025-24988', 'confidence': 0.28, 'patch_store_uid': '13c1e87d-a065-4d51-b526-a46f95d9c939', 'file': 'usbvideo.sys', 'date': 1751865237.720494, 'change_count': 27} +-------------------------------------------------------------------- +CVE-2025-24988 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +usbvideo.sys – Windows USB Video Class (UVC) kernel-mode driver +responsible for enumerating and operating USB camera devices. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read due to missing length validation while parsing +variable-size USB descriptors (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +During device enumeration usbvideo.sys scans the USB configuration +buffer that a camera reports. Many helper routines – e.g. +CountTopologyComponents, GetVideoSpecificDescriptor, GetEndpoint +Descriptor, GetStatusInterruptEndpointIndex, BuildUSBVideoFilter +Topology, … – walk the descriptor chain by adding the current +bLength ( + i = (UCHAR*)((UCHAR*)i + i->bLength) +) until a termination condition is met. In the vulnerable build the +code only checked the **first** descriptor retrieved via +USBD_ParseDescriptors and thereafter trusted every embedded +bLength. If a crafted descriptor advertises a length that extends +past the end of the real configuration buffer the pointer arithmetic +moves outside the caller-supplied memory. Subsequent reads (and in a +couple of sites writes) are therefore performed on unrelated kernel +memory. + +The defect is reachable from user mode with **no special +privileges** provided the attacker can insert (or emulate) a USB +camera. The offending value is fully attacker-controlled: it is the +bLength field inside the malicious descriptor. + +Individual functions add further, related flaws such as trusting a +UnitID < 8/0x16 and blindly indexing arrays allocated for the number +of units; these are blocked in the patch by explicit range checks. + +Affected parameters / structures + • ConfigurationDescriptor->wTotalLength + • Each UVC class-specific descriptor’s bLength and bDescriptorType + • Unit IDs copied into driver arrays (GetUnit*/Process* functions) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +for ( i = USBD_ParseDescriptors(v12, total, v12, 36); ; + i = (UCHAR *)((uint64)&i[*i] & -(uint64)(&i[*i] < end)) ) +{ + if (!i) break; // no bounds check after this point + ... +} + +// after +v11 = USBParseDescriptorWrapper(cfg, total, v8, 36, 12); +while (v11) { + ... // uses wrapper again + v11 = USBParseDescriptorWrapper(cfg, total, + (DWORD)v11 + v11->bLength, + 36, 12); +} +``` +`USBParseDescriptorWrapper` rejects a descriptor if + • bLength == 0, + • descriptor end crosses wTotalLength, + • requested subtype does not match. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +PnP start → SelectDeviceConfiguration → BuildUSBVideoFilterTopology → +CountTopologyComponents / GetVideoSpecificDescriptor … → crafted +bLength makes pointer walk past end → kernel reads memory outside the +USB buffer. + +Attack Vector +-------------------------------------------------------------------- +Physical or emulated USB camera that advertises a malformed UVC +configuration descriptor. No additional privileges are required; +Windows automatically enumerates the device when inserted. + +Patch Description +-------------------------------------------------------------------- +1. Replaced direct pointer arithmetic with + USBParseDescriptorWrapper(), a helper that verifies that the next + descriptor fits inside the original wTotalLength. +2. Added explicit *UnitID* upper-bound checks (`if (*a2 < 7/0x16/9) + return STATUS_INVALID_PARAMETER`). +3. Added consistent error returns (0xC0000245 / 0xC0000005) instead + of continuing on failure. +4. Updated all descriptor-parsing helpers to use the wrapper when the + new feature flag `Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage` + is enabled (enabled by default after patch). +5. Added fallback logging but aborted processing on validation + failure instead of proceeding. + +Security Impact +-------------------------------------------------------------------- +A local attacker that can attach a malicious USB UVC device can force +kernel-mode code to read beyond the supplied configuration buffer. +Information disclosure and, in some cases, memory corruption that can +be turned into elevation of privilege (EoP) are possible. The issue +is assigned CVE-2025-24988 and was rated Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +All descriptor walks now go through a single length-checked helper; +any attempt to supply an over-long bLength is rejected and the driver +returns STATUS_INVALID_PARAMETER. UnitID and endpoint counts are +validated before use. No remaining unchecked arithmetic paths were +observed in the patched diff, so the fix is considered complete and +effective. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24991_ntfs.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24991_ntfs.sys.txt new file mode 100644 index 0000000..5c9c3f8 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24991_ntfs.sys.txt @@ -0,0 +1,108 @@ +{'file': 'ntfs.sys', 'change_count': 17, 'confidence': 0.28, 'kb': 'KB5053598', 'patch_store_uid': 'e7ca9e07-1f84-451f-a10b-8a79bb74c0cb', 'cve': 'CVE-2025-24991', 'date': 1751829811.7602887} +-------------------------------------------------------------------- +CVE-2025-24991 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows NTFS kernel driver (ntfs.sys) – path-parsing and +restart-log routines: NtfsFindStartingNode, DoAction, and +NtfsInitializeUpcaseInfo. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read / information disclosure (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several internal NTFS helpers calculate buffer lengths and flag words +coming from user-supplied path names or from on-disk structures. In +NtfsFindStartingNode the variable that stores the FileNameFlags / +Ccb->Flags combination (v21) was declared as 64-bit (__int64). The +code then copied the 32-bit field (*((DWORD*)a8+39)) into this 64-bit +location and performed bit tests and mask/clear operations: + + v21 = *((unsigned int *)a8 + 39); + if ((v21 & 0x50) == 0 && (v21 & 0x20) != 0) … + +Because v21 was 64-bit, the compiler sign-/zero-extended the 32-bit +value; later comparisons mixed the widened value with 32-bit constants +and were used to derive a path component length that is stored in +*WORD a8[8]. When the upper dword contained non-zero data the length +calculation silently overflowed, producing a length up to 0xffff’xxxx +bytes long. Subsequent memmove and ExAllocatePoolWithTag calls copied +or allocated only the lower 16-bit element, while loops iterated over +the full (corrupted) 32-/64-bit size – reading beyond the caller’s +buffer into kernel memory. + +Exactly the same width-truncation pattern existed in other helper +functions: + • DoAction – several index-buffer routines used 64-bit temporaries + to hold 32-bit size fields taken from log-records. + • NtfsInitializeUpcaseInfo – 64-bit multiplications on DWORD array + indexes could walk past NtfsKnownUpcaseInfoArray. + +All three places could therefore read arbitrary kernel memory that is +subsequently returned to user mode (e.g. through STATUS_OBJECT_NAME_* +error buffering or via the log-file replay path). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +__int64 v21 = *((unsigned int *)a8 + 39); // sign/zero-extended +… if ((v21 & 0x400) != 0) { … } + +// after +int v21 = *((_DWORD *)a8 + 39); // width fixed +… unsigned int v24 = v22 & 0xFFFFFBFF; // mask safely +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User thread opens/creates a crafted path on an NTFS volume. +2. IoCreateFile → NtfsCommonCreate → NtfsFindStartingNode. +3. The path component length field overflows due to widened flag word + (step ➊ above). +4. memmove() in NtfsFindStartingNode copies data past the end of the + user-supplied name buffer, leaking uninitialised kernel memory in + the resulting UNICODE_STRING returned to user. +5. Similar over-reads are reachable during NTFS log replay via + DoAction, which is executed in kernel context while mounting a + dirty volume. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker provides a specially crafted long file +name (or a malicious NTFS image) that abuses the flag-width +confusion. No administrator rights are required; any process that can +access an NTFS volume can trigger the path parser. + +Patch Description +-------------------------------------------------------------------- +Microsoft changed all affected temporaries from 64-bit to 32-bit types +(int / unsigned int). Additional size validation has been inserted: +• New checks against *WORD <= 0x1FE before memmove. +• Early bail-out when Feature_4251462969 is enabled (Upcase helper). +• Extra length sanity ( (length > 2 && last char == '\\') ) before + stripping component. +These modifications prevent the length overflow and therefore stop the +out-of-bounds read. + +Security Impact +-------------------------------------------------------------------- +Before the patch, any local user could obtain arbitrary kernel stack +or pool contents that reside directly after a user-controlled UNICODE +buffer. Leaked pointers and structure contents break KASLR and aid in +further privilege-escalation exploits. The issue is classified as +Information Disclosure with CVSS base 5.5 (AV:L/AC:L/PR:L/UI:N/S:U/C:H +/I:N/A:N). + +Fix Effectiveness +-------------------------------------------------------------------- +The explicit down-cast to 32-bit together with additional length +sanity checks remove the integer-width confusion. All code paths that +previously consumed the widened value now operate on the correct field +size, stopping the overflow. No alternative uncontrolled path was +found in the updated routines, so the patch is deemed effective, though +other similar width-mismatches may still exist elsewhere in ntfs.sys. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24992_ntfs.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24992_ntfs.sys.txt new file mode 100644 index 0000000..82e56fb --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24992_ntfs.sys.txt @@ -0,0 +1,106 @@ +{'date': 1751831482.6771922, 'patch_store_uid': 'e7ca9e07-1f84-451f-a10b-8a79bb74c0cb', 'file': 'ntfs.sys', 'confidence': 0.14, 'cve': 'CVE-2025-24992', 'change_count': 17, 'kb': 'KB5053598'} +-------------------------------------------------------------------- +CVE-2025-24992 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows kernel driver ntfs.sys – restart-area processing +logic inside ReadRestartTable() and callers during volume mount and +log-file recovery. + +Vulnerability Class +-------------------------------------------------------------------- +Buffer Over-read (CWE-126) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NTFS mounts or recovers a volume by reading the first restart log +record from $LogFile and then extracting the embedded Restart Table. + +Before the patch ReadRestartTable() trusted the 16-bit field at offset ++0x04 of the restart record (RestartTableOffset). The code used that +value directly: + table_ptr = record_base + *(USHORT *)(record_base+4); + table_len = RestartAreaLength – RestartTableOffset; + NtfsCheckRestartTable(table_ptr, table_len,…); + +No check guaranteed that RestartTableOffset was large enough to cover +the fixed 40-byte header or the variable Update Sequence Array. A +crafted log record could set the offset to a value smaller than the +minimal header size (or even to 0). NtfsCheckRestartTable then walked +entries beyond the caller-supplied buffer, causing a linear read past +the end of the mapped log page held in non-paged pool. + +Because the over-read data is later copied into user-controlled error +buffers or can be returned through STATUS_DISK_CORRUPT bug-check +information, an attacker with write access to a raw NTFS image can +disclose adjacent kernel memory. On debug builds the stale read may +also raise an access violation leading to a system crash. + +Affected parameters / structures + • RESTART_AREA.RestartOffset (ushort @ +0x04) + • Update Sequence Array count (ushort @ +0x0E) + • restart table body pointed to by the untrusted offset + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (!NtfsCheckRestartTable( + rec+*(USHORT *)(rec+4), // unchecked offset + RestartLen-*(USHORT *)(rec+4), // unchecked length + ctx)) + NtfsRaiseStatusInternal(...); + +// after +header = *(USHORT *)(rec+14); // entry count +safe_ofs = (header<=1 ? 40 : 8*header+32); // min hdr size +if (!NtfsCheckRestartTable( + rec+safe_ofs, // validated offset + RestartLen-*(USHORT *)(rec+4), + ctx)) + goto corrupted_path; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Mount / boot → NtfsRestartVolume → InitializeRestartState → +ReadRestartTable → NtfsCheckRestartTable (over-read occurs) + +Attack Vector +-------------------------------------------------------------------- +A local, low-privilege attacker supplies or mounts a malicious NTFS +volume (e.g., VHD, USB, or raw disk) whose $LogFile contains a crafted +restart record with a small RestartTableOffset. When Windows mounts +or checks the volume the driver processes the record and performs the +out-of-bounds read. + +Patch Description +-------------------------------------------------------------------- +• Recomputes a minimum safe offset (40 bytes or 32+8*N) from the + Update-Sequence-Array count instead of trusting the on-disk + RestartTableOffset. +• Adds a secondary validation branch gated by + Feature_1347162426__private_IsEnabledDeviceUsageNoInline(). +• Introduces additional corruption-handling labels (LABEL_14 / 17) that + raise STATUS_DISK_CORRUPT rather than continuing with invalid data. +• Updates related callers (InitializeRestartState, NtfsAbortTransaction + etc.) to use the hardened helper and new ETW guids. + +Security Impact +-------------------------------------------------------------------- +Successful exploitation lets an attacker read beyond the mapped +restart record buffer, potentially disclosing uninitialized kernel +memory, and may also cause a bug-check (0x24 or 0xC0000225) leading to +a DoS during system boot or volume attach. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes all trust in the attacker-controlled offset and +performs explicit size calculations before touching the restart table. +All corruption cases now terminate with STATUS_DISK_CORRUPT, stopping +further processing. No remaining uncontrolled reads from the restart +record have been observed, indicating the patch fully mitigates the +reported CWE-126 issue. + diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24993_ntfs.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24993_ntfs.sys.txt new file mode 100644 index 0000000..bde25a7 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24993_ntfs.sys.txt @@ -0,0 +1,140 @@ +{'patch_store_uid': 'e7ca9e07-1f84-451f-a10b-8a79bb74c0cb', 'date': 1751831476.842607, 'change_count': 17, 'confidence': 0.37, 'file': 'ntfs.sys', 'cve': 'CVE-2025-24993', 'kb': 'KB5053598'} +-------------------------------------------------------------------- +CVE-2025-24993 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows NTFS kernel driver (ntfs.sys). +Affected routines process the NTFS restart area while a volume is +mounted or recovered: + • ReadRestartTable() + • InitializeRestartState() + • NtfsInitializeUpcaseInfo() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by insufficient bounds validation +(CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. While mounting / recovering an NTFS volume, ntfs.sys reads a log + record that contains a variable-length RESTART TABLE. The parser + is implemented by ReadRestartTable(). + +2. Before the fix the routine performed only two integrity checks: + NtfsCheckLogRecord() + NtfsCheckRestartTable(base + Offset, Size, ctx) + where Offset is *(USHORT*)(hdr+4) and Size is + LogRecordLength – Offset. + +3. Immediately afterwards it derived another internal offset that + depends on the restart table entry count (EntryCount): + EntryHdr = *(USHORT*)(base + 14) + Variable = 8 * EntryHdr + 32 (or 40 if EntryHdr<=1) + NumberOfBytes = LogRecordLength – Variable (A) + +4. Neither EntryHdr nor Variable was validated. If an attacker stores + an oversized EntryHdr in the on-disk log record, expression (A) + underflows, producing a very large unsigned length. NumberOfBytes + is returned to the caller (InitializeRestartState) where it is + used as the allocation size: + ExAllocatePoolWithTag(NonPagedPoolNx, NumberOfBytes, …) + memmove(dst, RestartTable, NumberOfBytes); + +5. The destination buffer is the requested large size, but the source + buffer lives inside a fixed 4-kB log page. The memmove therefore + reads far past the end of the mapped log record and copies the + data into kernel heap. The read overflow is immediately followed + by attacker-controlled writes when subsequent NTFS bookkeeping + code manipulates the over-sized heap buffer. Crafted data can be + used to corrupt pool metadata and gain arbitrary kernel code + execution. + +6. Additional unchecked tables are later copied in + InitializeRestartState() and NtfsInitializeUpcaseInfo(); the patch + adds parallel mitigations there to catch malformed indexes and + version fields that previously permitted further heap corruption. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – ReadRestartTable (excerpt) +v10 = *(unsigned __int16 *)(v9 + 14); +v11 = 8 * v10 + 32; +if ((unsigned __int16)v10 <= 1) + v11 = 40; +*a5 = *(_DWORD *)(v5 + 64) - v11; // may under-flow +return v9 + v11; + +// AFTER – new size validation +v11 = *(unsigned __int16 *)(v10 + 14); +v12 = 8 * v11 + 32; +if ((unsigned __int16)v11 <= 1) + v12 = 40; +if (!NtfsCheckRestartTable(v10 + v12, + *(_DWORD *)(a4 + 64) - *(USHORT *)(v10 + 4), ctx)) + /* corruption path */ +*a5 = *(_DWORD *)(a4 + 64) - v12; +return v10 + v12; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User forces Windows to mount a crafted NTFS volume (USB stick, VHD, + remote iSCSI, etc.). +2. NtfsMountVolume → NtfsRestartVolume → InitializeRestartState → + ReadRestartTable. +3. Malicious restart log page is parsed; EntryCount triggers arithmetic + underflow, NumberOfBytes becomes huge. +4. InitializeRestartState allocates a large heap buffer and performs + memmove(), reading past the end of the log page and corrupting heap + data. +5. Subsequent NTFS operations operate on the corrupted structures, + allowing attacker-controlled memory writes and eventual arbitrary + kernel code execution. + +Attack Vector +-------------------------------------------------------------------- +Any attacker who can supply an NTFS image to a victim machine can +exploit the bug. Typical vectors: + • Plugging in a malicious removable drive. + • Mounting a malicious VHD / ISO. + • Opening a network share that exposes a crafted NTFS volume. +No prior privileges are required; the vulnerability is triggered during +automatic mount processing in the kernel. + +Patch Description +-------------------------------------------------------------------- +• Added an early size-aware call to NtfsCheckRestartTable() that uses + the computed Variable offset (header+array) before returning control + to callers. +• Guarded the new check behind Feature_* runtime flags for rollout. +• Added multiple new corruption handlers (labels LABEL_14 / _17 etc.) + that raise STATUS_DISK_CORRUPT_ERROR instead of continuing. +• InitializeRestartState() now performs: + – Revalidation of copied restart tables. + – Index-range enforcement when moving individual table entries. + – Extensive duplicate / overlap detection. +• NtfsInitializeUpcaseInfo() now clamps the version fields and clears + them when a new feature flag is enabled. + +Security Impact +-------------------------------------------------------------------- +Before the fix a crafted NTFS log could cause an out-of-bounds read +followed by controlled heap corruption inside the kernel non-paged +pool, leading to local or remote (via removable media) kernel-mode +arbitrary code execution (RCE). Successful exploitation yields SYSTEM +privileges and full OS compromise. + +Fix Effectiveness +-------------------------------------------------------------------- +The added NtfsCheckRestartTable() invocation prevents the arithmetic +underflow by verifying that (Variable <= LogRecordLength). All paths +that previously used the unchecked NumberOfBytes now bail out with +STATUS_DISK_CORRUPT_ERROR if the table is malformed. Additional +integrity checks in InitializeRestartState and NtfsInitializeUpcaseInfo +close secondary heap-corruption avenues. Provided the checks are +correct and Feature_* gates are enabled system-wide, the original +overflow is fully mitigated. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24994_cdp.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24994_cdp.dll.txt new file mode 100644 index 0000000..2281d7f --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24994_cdp.dll.txt @@ -0,0 +1,127 @@ +{'patch_store_uid': '3f49ae9d-f63d-4771-89cb-ee0eed639f83', 'date': 1751831486.206324, 'file': 'cdp.dll', 'cve': 'CVE-2025-24994', 'kb': 'KB5053598', 'change_count': 84, 'confidence': 0.11} +-------------------------------------------------------------------- +CVE-2025-24994 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Cross-Device Platform (cdp.dll); specifically the two helper +routines + • shared::ActivityPolicies::CanPerformOperation(ActivityData&, …) + • shared::ActivityPolicies::CanPerformOperation(CDPActivityType,…) +Both are used by the Cross-Device Service (CDPSvc) to decide whether a +caller may execute a requested activity (sync, backup/restore, etc.) +under the current enterprise policy set. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control – CWE-284 + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CDPSvc keeps an in-memory tree of ActivityPolicy objects. Each policy +node contains, among other fields: + +0x20 WORD LengthOfPermissionScopeString (offset 0x32 in diff) + +0x28 char* PermissionScopeString (offset 0x48 in diff) + +0x28 DWORD ActivityType (offset 0x40 in diff) + +0x50 WORD BlockMask (offset 0x80 in diff) + +When a caller requests an operation, CanPerformOperation() retrieves +the caller-provided permission scope string into the local std::string +v26 and walks the policy tree. Prior to the patch the check was: + + if (policy->ActivityType == Request.ActivityType) { + if (IsActivityPermissionScopeAffectedByPolicy(v26, + policy->Scope)) { + if ((Request.BlockFlags & policy->BlockMask) == …) + deny(); + } + } + +Critically, *no* guard existed to make sure that the policy’s +PermissionScopeString was actually populated. Enterprise-State +Roaming (ESR) policies are stored with Scope == "" (length == 0). The + helper IsActivityPermissionScopeAffectedByPolicy() treats an empty +string as a wildcard, so an ESR policy applied to ActivityType X would +also silently apply to the Backup/Restore permission scope although +the admin never intended that. Because Backup/Restore operations are +executed in a higher-privilege context, a low-privileged user could +leverage the unintended wildcard match to make CDPSvc believe a +high-privilege Backup/Restore operation was *not* blocked, thereby +gaining elevated capability. + +Both overloads of CanPerformOperation() therefore allowed the bypass. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old logic (before) +while (!policy->IsDeleted) { + if (policy->ActivityType == RequestedActivityType) { + // EMPTY policy->Scope matches everything! + if (IsActivityPermissionScopeAffectedByPolicy(reqScope, + policy->Scope)) { + if ((reqBlockMask & policy->BlockMask) == reqBlockMask) + deny(); // v7 / v6 = 0 + } + } + ++it; +} + +// New logic (after) +if (Feature_SeparateBRFromESR.Enabled() && + policy->LengthOfPermissionScopeString == 0 && + IsBackupRestorePermissionScope(reqScope)) { + Log("Ignore EnterpriseStateRoaming policy for permission scope"); + // skip this policy node +} else if (policy->ActivityType == RequestedActivityType) { + ... (unchanged access check) ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged code requests a Backup/Restore CDP operation. +2. CDPSvc invokes CanPerformOperation(). +3. Function pulls the caller’s permission scope string ("br_scope") + from the caller-supplied app-ID. +4. The policy tree is enumerated; the first ESR policy node has + PermissionScopeString length == 0. +5. Empty string matches "br_scope" ➜ access check passes ➜ operation + proceeds in service context. +6. Attacker gains capabilities reserved for higher-privileged + principals (EoP). + +Attack Vector +-------------------------------------------------------------------- +Local attacker already able to call CDPSvc IPC endpoints (any +authenticated user). By crafting a Backup/Restore request the service +mistakenly authorises the operation despite enterprise policy. + +Patch Description +-------------------------------------------------------------------- +The update introduces a new feature flag +Feature_SeparateBRFromESR (2578215227). When the flag is enabled the +code path adds an early filter in both overloads: + • If the policy entry’s PermissionScopeString length is zero *and* + the caller’s requested scope is recognised as Backup/Restore, the + policy node is ignored and a diagnostic message is logged. All + other policies are evaluated as before. + +Security Impact +-------------------------------------------------------------------- +The wildcard match for empty-scope ESR policies allowed Backup/Restore +operations, which run with elevated service privileges, to bypass +administrative intent. An authenticated local attacker could therefore +escalate privileges (EoP). The patch restores proper separation of +policy scopes and closes the privilege-escalation path. + +Fix Effectiveness +-------------------------------------------------------------------- +Effective only when Feature_SeparateBRFromESR is turned on; roll-out is +controlled by a WIL feature switch. With the feature enabled, empty +permission-scope policies can no longer influence Backup/Restore +requests, removing the improper access. No memory-safety or logic +regression is observable from the diff, and new logging aids further +validation. Residual risk: environments where the feature flag is +left disabled remain vulnerable. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24995_ksthunk.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24995_ksthunk.sys.txt new file mode 100644 index 0000000..eb25228 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24995_ksthunk.sys.txt @@ -0,0 +1,117 @@ +{'cve': 'CVE-2025-24995', 'change_count': 3, 'confidence': 0.62, 'patch_store_uid': '3e5bdaf1-34d7-4c95-a38c-0592491ee613', 'file': 'ksthunk.sys', 'kb': 'KB5053598', 'date': 1751831366.416786} +-------------------------------------------------------------------- +CVE-2025-24995 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Kernel-mode driver ksthunk.sys (Kernel Streaming WOW Thunk +Service – used by 32-bit applications to reach 64-bit KS filters). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by signed/unsigned and 32-bit/64-bit +integer-overflow errors in length calculations that precede pool +allocations and subsequent memory moves. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Three IOCTL helper paths relied on 32-bit arithmetic when computing +buffer sizes, offsets, and running totals. The variables involved were +later used to allocate pool memory but were re-used as SIZE_T sources +for memmove / Probe* APIs, leading to a classic alloc-smaller / copy- +bigger scenario. + +1. CKSAutomationThunk::ThunkDisableEventIrp + Size field at (EventHeader-56) is incremented by 16: + v13 = v11 + 16; + The only wrap-around check was gated behind the feature flag + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_5(). When the + flag was disabled the addition could overflow (v13 < v11) and the + corrupted length was written back, letting later callers use an + arbitrarily small length that still points inside attacker-supplied + data. + +2. CKSAutomationThunk::ThunkEnableEventIrp + OutSize (32-bit) is aligned then added to InputSize to form the + allocation length: + v12 = (OutSize + 23) & ~7; + ExAllocatePool2(…, v12 + v9, …); + The expression was evaluated with 32-bit temporaries; values near + 0xFFFF_FFFF wrapped, producing a tiny allocation. memmove then + copied v9 bytes (trusted) plus OutSize bytes (attacker controlled) + into the undersized buffer, overflowing the look-aside pool. + +3. CKSThunkPin::ThunkStreamingIrp + The per-packet loop maintains two counters: remaining stream buffer + (v6) and totalBytesToRead (v53). Both were unsigned 32-bit. A + crafted KSSTREAM_HEADER whose Size field exceeds 4 GB causes: + v53 += ExtendedHeader + 56; + to wrap and later pass the truncated length to + ExAllocatePoolWithTag, followed by memcpy of the real, much larger + stream, smashing the heap. The patch moves the counters to SIZE_T, + adds explicit (newCounter < oldCounter) checks, and introduces + RtlLogUnexpectedCodepath() for anomalous headers. + +In every case the overflowed heap buffer resides in kernel, letting a +local attacker overwrite adjacent pool metadata or objects and achieve +arbitrary code execution in ring-0. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ThunkEnableEventIrp – before +v12 = ((_DWORD)OutSize + 23) & 0xFFFFFFF8; // 32-bit wrap +ExAllocatePool2(…, v12 + v9, …); // tiny alloc +memmove((void *)(v12 + buf), src, v9); // large copy + +// ThunkDisableEventIrp – before +v13 = v11 + 16; // may wrap +if (!IsEnabledFlag && v13 < v11) { … } // check skipped +*(_DWORD *)(hdr-56) = v13; // corrupt length + +// ThunkStreamingIrp – before +v53 += ExtendedHeaderSize + 56; // 32-bit add +if (v53 + 56 < v53) ExRaiseStatus(...); // missing +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User mode → DeviceIoControl(<KS pin>, IOCTL_KS_WRITE_STREAM, …) + ↳ ksthunk!CKSThunkPin::DispatchIoctl + ↳ ThunkStreamingIrp / ThunkEnableEventIrp / ThunkDisableEventIrp + ↳ attacker-controlled buffer processed as shown above + ↳ integer wrap → undersized pool allocation + ↳ memmove / memcopy overruns pool → EoP. + +Attack Vector +-------------------------------------------------------------------- +Any local, authenticated user able to open a KS pin exposed through the +WOW thunk service can send crafted IOCTL_KS_WRITE_STREAM / ENABLE_EVENT +/ DISABLE_EVENT requests containing malicious length fields to trigger +the overflow. + +Patch Description +-------------------------------------------------------------------- +• Replaced 32-bit arithmetic with SIZE_T / 64-bit variables. +• Added wrap-around checks using pattern (newSize < oldSize). +• Made checks unconditional by removing dependency on feature flags. +• Added extra ProbeForRead/Write validation of metadata pointers. +• Introduced RtlLogUnexpectedCodepath() to record malformed headers. +• Updated function prototypes to use NTSTATUS* and const-correct types. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could corrupt kernel heap memory and +execute arbitrary code in kernel context, achieving elevation of +privilege (ring-0). The overflow occurs in the kmalloc pool, so SMEP/ +SMAP do not mitigate it. + +Fix Effectiveness +-------------------------------------------------------------------- +The new size calculations are done in 64-bit space and every critical +addition is followed by a wrap-around test, eliminating the immediate +integer-overflow vector. Additional pointer validation and logging +should detect malformed structures early. No residual unchecked size +additions were observed in the patched diff, so the fix appears +comprehensive for the affected paths. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24996_ntlmshared.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24996_ntlmshared.dll.txt new file mode 100644 index 0000000..8bbfcc8 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24996_ntlmshared.dll.txt @@ -0,0 +1,117 @@ +{'patch_store_uid': 'cf73d068-b97f-4a64-a5ea-2a1cb78b4f56', 'file': 'ntlmshared.dll', 'confidence': 0.25, 'date': 1751865204.9416416, 'cve': 'CVE-2025-24996', 'change_count': 1, 'kb': 'KB5053598'} +-------------------------------------------------------------------- +CVE-2025-24996 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NTLM authentication library (ntlmshared.dll) – helper routine +wil::details::FeatureImpl<__WilFeatureTraits_Feature_2032163131>:: +GetCachedFeatureEnabledState() + +Vulnerability Class +-------------------------------------------------------------------- +Logic / state-management error (feature flag bit-mask mishandling) +leading to security-feature bypass – maps best to CWE-73 secondary +effect, but primary coding flaw is an Incorrect Bitmask Calculation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GetCachedFeatureEnabledState() maintains a 32-bit cache word that +encodes several independent feature-state sub-flags: + 0x0001 – cached "enabled" indicator (LSB) + 0x0040 – FEATURE_ENABLED returned by policy engine + 0x0400 – variant/group bit A + 0x0800 – variant/group bit B + ... – other bits (interest not changed by patch) + +Before the patch the routine collapsed those bits with the single +statement + v13 = v10 | (v10 >> 6) & 1; +(v10 already contained the 0x40/0x400/0x800 encoding). The right +shift copies bit 6 (0x40) straight into bit 0, unconditionally marking +the feature as fully "enabled" whenever 0x40 is present, even if the +higher order bits indicate that the feature is only partially rolled +out or explicitly disabled. + +Consumers – including MsvpPasswordValidate() in the NTLM package – +subsequently test bit 0 to decide whether modern protection logic +(e.g. channel binding and hash-suppression) must be applied. Because +bit 0 was set too broadly, callers deemed the security feature active +while the underlying policy still allowed legacy behaviour. The +resulting mismatch let remote peers force the code down the insecure +path and obtain NTLM hashes that should have been withheld. + +Patch logic: + • Derive a temporary flag v12 that becomes TRUE only when *both* + 0x400 and 0x800 are set *and* 0x40 indicates Enabled. + • Build the new cache word as + v15 = (v12 ? 1 : 0) | v10; + therefore bit 0 is asserted only under the stricter condition. + • A fast-path early return was added when the cache already contains + the final 0b110 state, reducing needless re-evaluation. +No other data structures or buffers are touched; the bug is pure +logic. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (before) +v10 = ...; // 0x40/0x400/0x800 composition +v13 = v10 | (v10 >> 6) & 1; // blindly propagates bit 6 to bit 0 +... +*(_DWORD *)a2 = v13; // cached and later queried by callers + +// fixed (after) +v10 = ...; +char v12 = 0; +if ( (v10 & 0xC00) == 0xC00 ) // require both higher bits + v12 = 1; +else if ( (v10 & 0x40) ) // 0x40 alone no longer enough + /* fall through */; +int v13 = v12 ? 1 : 0; +... +v15 = v13 | v10; // strict enable decision +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Remote NTLM negotiation + -> LSASS!MsvpPasswordValidate() + -> ntlmshared!GetCachedFeatureEnabledState() + (returns incorrectly formed cache word) + -> Caller trusts bit0 == 1, skips modern protection + -> Attacker receives or forges NTLM hash / proof + +Attack Vector +-------------------------------------------------------------------- +A network attacker able to pose as or redirect a server that requests +NTLM authentication convinces the victim host to authenticate. Because +the feature state is mis-cached, the client believes protection is +active while actually sending legacy hash material, enabling spoofing +or relay attacks. + +Patch Description +-------------------------------------------------------------------- +1. Added quick return when cache already in stable state. +2. Re-implemented derivation of LSB ("enabled" bit): + • Now requires 0x400 and 0x800 *plus* 0x40, instead of 0x40 alone. +3. Refactored variable names and inserted helper (char v12) for + clarity; the surrounding atomic-compare-exchange loop is unchanged. +No memory layout or API surface modifications. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a remote, unauthenticated attacker could obtain or +relay NTLM hashes, defeating channel-binding protections and enabling +credential spoofing. The flaw affects any client or service relying +on the cached feature flag to decide whether to send secure NTLM +responses. + +Fix Effectiveness +-------------------------------------------------------------------- +The new mask explicitly checks the previously ignored 0x400/0x800 +state bits, eliminating the false-positive enablement. Because the +code path is otherwise identical and no unsafe memory operations were +introduced, the patch fully neutralises the logic flaw. No residual +bypass is evident in the diff. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24997_dxgkrnl.sys.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24997_dxgkrnl.sys.txt new file mode 100644 index 0000000..8d8c998 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-24997_dxgkrnl.sys.txt @@ -0,0 +1,105 @@ +{'date': 1751831496.5763185, 'patch_store_uid': 'a609137f-6c45-481b-84ad-293a321dd24d', 'change_count': 39, 'kb': 'KB5053598', 'file': 'dxgkrnl.sys', 'confidence': 0.29, 'cve': 'CVE-2025-24997'} +-------------------------------------------------------------------- +CVE-2025-24997 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – dxgkrnl.sys (DirectX Graphics Kernel) +Sub-component: object/property handling (CProperty) + +Vulnerability Class +-------------------------------------------------------------------- +NULL-pointer dereference (CWE-476) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each logical property in the graphics kernel is represented by a +CProperty object whose second pointer-sized field (+0x08) holds the +address of a ReferenceCounted value. When the property is deleted or +re-initialised the value must be released and the field cleared. + +Before the patch the clean-up routine was implemented in +CJScript9Holder::ReinitializeOrClearObject(), which executed only when +Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_23() returned true. +Instead of loading the stored value pointer, the routine blindly +passed the CProperty ("this") pointer to +DxgMonitor::USB4POFXREF_REF_ACCESSOR::Release: + + DxgMonitor::USB4POFXREF_REF_ACCESSOR::Release(a1); + +Because a1 is **not** a valid USB4POFXREF_REF_ACCESSOR object, the +callee immediately dereferenced random/NULL memory. If the stored +value pointer was NULL – the most common state when a property was +never initialised – the access happened at address 0x0, raising bug +check 0x50 (PAGE_FAULT_IN_NONPAGED_AREA) and crashing the system. + +Thus any execution path that destroyed a CProperty while the feature +flag was enabled could instantly trigger a kernel-mode NULL pointer +read, resulting in a system-wide denial of service. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +void CJScript9Holder::ReinitializeOrClearObject(CBase *a1, CMarkup *a2) +{ + if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_23()) + DxgMonitor::USB4POFXREF_REF_ACCESSOR::Release(a1); // wrong ptr +} + +// after +void CProperty::FreeValue(CProperty *this) +{ + ReferenceCounted *val = *((ReferenceCounted **)this + 1); // +0x08 + if (val) + { + ReferenceCounted::Release(val); + *((ReferenceCounted **)this + 1) = nullptr; // clear field + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode application issues a D3DKMT/IOCTL or API that removes a + device property. +2. Kernel enters dxgkrnl!CProperty::FreeValue (old name + CJScript9Holder::ReinitializeOrClearObject). +3. Feature flag is enabled ➔ function calls Release() with the **this** + pointer instead of the property value pointer. +4. Release() dereferences the invalid/NULL pointer ➔ bug-check ➔ system + crash. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running graphics APIs. No additional +privileges are required; the bug can be triggered from any session +that is able to create and delete DirectX properties (e.g. via +D3DKMTCreateAllocation + D3DKMTDestroyAllocation sequences). + +Patch Description +-------------------------------------------------------------------- +The vulnerable routine has been replaced by a correctly named +CProperty::FreeValue() implementation that: +1. Loads the stored value pointer from offset +0x08. +2. Checks the pointer for NULL before use. +3. Calls ReferenceCounted::Release() with the correct argument. +4. Clears the member to prevent stale references. + +The erroneous call to DxgMonitor::USB4POFXREF_REF_ACCESSOR::Release() +was completely removed. + +Security Impact +-------------------------------------------------------------------- +Before the patch any attempt to free an uninitialised or already +cleared property caused an immediate kernel crash, leading to a local +denial of service (system reboot) but no privilege escalation or +information disclosure. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code path validates the pointer and uses the appropriate +release helper, eliminating the NULL dereference. No remaining code +paths in CProperty::FreeValue dereference unchecked pointers, so the +patch fully mitigates CVE-2025-24997. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26633_mmc.exe.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26633_mmc.exe.txt new file mode 100644 index 0000000..a0b0519 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26633_mmc.exe.txt @@ -0,0 +1,131 @@ +{'kb': 'KB5053598', 'change_count': 4, 'cve': 'CVE-2025-26633', 'confidence': 0.2, 'patch_store_uid': '3ba97ef7-aa43-419e-99be-296dcef29324', 'file': 'mmc.exe', 'date': 1751831459.3739293} +-------------------------------------------------------------------- +CVE-2025-26633 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Management Console (mmc.exe) – feature-flag handling code + in wil::details::FeatureImpl::<T>::GetCurrentFeatureEnabledState() + and the console loading routine CAMCDoc::ScOnOpenDocument(). + +Vulnerability Class +-------------------------------------------------------------------- +Security-feature bypass / logic error (CWE-707 Improper Neutralization). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +MMC relies on Windows Internal Lib (wil) feature flags to decide if +additional protections ("Block opening untrusted .msc files") must be +applied. For every feature, wil converts the raw +FEATURE_ENABLED_STATE value returned by WilApi_GetFeatureEnabledState() +into an internal 32-bit bitfield that is later queried through +__private_IsEnabled(). + +Buggy algorithm (pre-patch): + 1. The helper zeroed the output buffer, forced bit 6 to 1 (0x40) or 0 + depending on partially-evaluated state, copied three other + condition bits to 0x400 / 0x800, and finally copied **bit 6 into + bit 0**: + dst = v7 | v8 | (((v7 | v8) >> 6) & 1); + 2. No verification was performed that policy-blocking bits (0x400 and + 0x800) were mutually exclusive with the enable bit (0x40). + +Result: if both policy bits (0x400|0x800 == 0xC00) and 0x40 were set the +last line always propagated an **Enabled = 1** flag even though policy +explicitly disabled the feature. __private_IsEnabled() therefore +returned FALSE (feature OFF) when it should have been TRUE (feature ON / +blocking active). + +CAMCDoc::ScOnOpenDocument() uses two wil features: + * 2578215227 – "BlockUntrustedConsoleFiles" + * 2408386874 / 220736827 – related companion switches +The routine checks IsEnabled() first and only denies loading a console +file when the feature is ON *and* the file originates from an +untrustworthy source (MOTW / URL ZONE). + +Because GetCurrentFeatureEnabledState() could erroneously clear the +feature-enabled bit, ScOnOpenDocument() concluded that the blocking +feature was disabled and proceeded to load the file, bypassing the +intended protection. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// mmc.exe, before patch (simplified) +*(_DWORD *)a2 = v7 | v8 | ((v7 | (unsigned)v8) >> 6) & 1; // bit-0 mirrors bit-6 +``` +```c +// after patch +v9 = v8 | v7; // merged flags +v10 = ((v9 & 0xC00) == 0xC00); // both policy bits? +... +if (!((v9 & 0x40) && v10)) // only set Enabled when legal + v11 = 0; +*(_DWORD *)a2 = v11 | v9; // correct final bitmap +``` +```c +// ScOnOpenDocument – old gating +if (!IsEnabled(BlockUntrusted) || !IsFileSourceUntrustworthy(path)) + load_file(); +``` +```c +// ScOnOpenDocument – new gating and fallback +if (!IsEnabled(BlockUntrusted)) goto normal_path; +if (!path || !*path) fail; +if (!_IsFileSourceUntrustworthy(path)) goto normal_path; +// otherwise: abort with SC_E_UNTRUSTED_SOURCE +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User double-clicks a malicious .msc file that carries a Mark-of-the- + Web (untrusted zone). +2. MMC invokes CAMCDoc::ScOnOpenDocument(). +3. ScOnOpenDocument() queries + FeatureImpl<BlockUntrustedConsoleFiles>::__private_IsEnabled(). +4. GetCurrentFeatureEnabledState() returns bitmap with bit-0 cleared due + to the faulty calculation even though policy demands blocking. +5. IsEnabled() therefore returns FALSE; the OR-clause in the caller is + satisfied and the console file is loaded normally. +6. Malicious snap-ins or configuration inside the .msc file execute with + the user’s privileges. + +Attack Vector +-------------------------------------------------------------------- +Local – the attacker convinces a victim to open a crafted console file +(.msc) that is tagged as coming from the Internet or another untrusted +location. No additional privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. Re-implemented the bit-aggregation logic in + GetCurrentFeatureEnabledState(): + • Separately tracks the raw FEATURE_ENABLED_STATE (v6). + • Validates conflicting combinations (0xC00 vs 0x40). + • Calculates the final "Enabled" bit only when the combination is + semantically valid. +2. Added extra sanity checks in ScOnOpenDocument(): + • Early exit when feature is disabled *before* any trust checks. + • Uses the correct file-path pointer type. + • Adds secondary condition that blocks if Feature 2408386874/​ + 220736827 is enabled while the primary feature is disabled. + • Consolidates error handling and resource cleanup. + +Security Impact +-------------------------------------------------------------------- +A malicious .msc file originating from an untrusted source could be +opened without warning, bypassing the intended protective feature. Once +loaded, MMC snap-ins can instantiate arbitrary COM objects, potentially +leading to code execution in the context of the current user. The issue +is therefore a local security-feature bypass with high exploitation +potential. + +Fix Effectiveness +-------------------------------------------------------------------- +The new bitmap construction enforces correct mutual exclusion between +policy bits and the enable bit, eliminating the scenario where a feature +is silently treated as disabled. Additional guard code in +ScOnOpenDocument() performs explicit source-trust checks independent of +feature-flag ambiguities. No residual bypass has been identified in the +patched paths based on the supplied diff. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26633_mmcndmgr.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26633_mmcndmgr.dll.txt new file mode 100644 index 0000000..be6ef57 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26633_mmcndmgr.dll.txt @@ -0,0 +1,148 @@ +{'file': 'mmcndmgr.dll', 'patch_store_uid': 'f10b5008-ac94-4569-9dc4-d635f7553c61', 'kb': 'KB5053598', 'cve': 'CVE-2025-26633', 'change_count': 16, 'date': 1751831455.8224468, 'confidence': 0.22} +-------------------------------------------------------------------- +CVE-2025-26633 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Management Console (mmcndmgr.dll) – private replacement of +StrSafe StringCchCopyA and its direct callers GetSubstituteHKLFromReg +and GetRegisteredLocation. + + +Vulnerability Class +-------------------------------------------------------------------- +Stack-based buffer overflow / incorrect boundary check that can be +abused as a Security-Feature-Bypass (CWE-121 mapped to CWE-707 in the +vendor advisory). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +mmcndmgr.dll contained a home-grown copy of StrSafe’s +StringCchCopyA(). The original prototype is + HRESULT StringCchCopyA(CHAR *dst, size_t dstChars, PCSTR src); + +The buggy implementation ignored the supplied length argument a2 and +instead hard-coded the copy limit to 260 (MAX_PATH). Pseudocode: + size = 260; + delta = src - dst; // establishes memcopy offset + while (size && *(src)) { // copy at most 260 bytes + *dst++ = *(src++); + --size; + } + *dst = 0; // terminator + return size==0 ? E_INSUFF : S_OK; + +Consequences: +1. If the real destination buffer is smaller than 260 bytes the loop + overruns the stack or heap, corrupting adjacent memory. +2. Callers that rely on the return code to detect truncation receive + false negatives because an over-large length parameter never limits + the copy. + +Two in-module callers pass attacker-controlled or otherwise +non-constant sizes: +• GetSubstituteHKLFromReg(char SubKey[272]) originally forwarded its + second argument a2 (a 16-bit language ID) as the length parameter + even though the array is 272 bytes long. The copy size could + therefore be anything between 0 and 65535. +• GetRegisteredLocation(char *OutPath) forwarded the uninitialised rdx + register (v3) as the length. From user-mode the register contents + depend on the call site and are uncontrolled inside the routine. + +Providing a destination buffer <260 bytes (for example by hooking a +public export that calls GetRegisteredLocation with a small stack +array) causes an out-of-bounds write that can smash the return address +and bypass the intended security boundary. + +Structures / parameters affected +• StringCchCopyA – parameter a2 (size) is ignored. +• SubKey[272] / OutPath – potential overflow targets on the stack. +• HRESULT semantics – incorrect success reporting masks the fault. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable implementation (before) +__int64 __fastcall StringCchCopyA(char *dst,__int64 len,const char *src) +{ + __int64 cnt = 260; // <-- hard-coded + __int64 delta = src - dst; + do { + if (cnt == -2147483386) break; + char ch = dst[delta]; + if (!ch) break; + *dst++ = ch; // overflows if dst <260 + --cnt; + } while (cnt); + *(cnt ? dst : dst-1) = 0; + return cnt==0 ? 0x8007007A : 0; +} +``` + +```c +// caller (before) +StringCchCopyA(SubKey, a2, "SOFTWARE\\Microsoft\\CTF\\TIP\\"); +``` + +```c +// fixed implementation (after, abridged) +int __fastcall StringCchCopyA(char *dst, size_t len, const char *src) +{ + int hr = StringValidateDestA(dst, len); + return (hr>=0) ? StringCopyWorkerA(dst,len,src) : hr; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker causes code path that ultimately calls + GetRegisteredLocation() or GetSubstituteHKLFromReg(). +2. Either routine invokes StringCchCopyA() with a destination buffer + that may be smaller than 260 bytes but with an arbitrary length + argument. +3. StringCchCopyA() copies up to 260 bytes regardless of the true + buffer size. +4. Out-of-bounds write corrupts saved registers/SEH, enabling control + of execution or bypass of MMC security checks. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker supplying crafted data to any MMC snap-in that ends up +calling the vulnerable helpers, or injecting a DLL that calls these +exports with undersized buffers. No special privileges are required. + + +Patch Description +-------------------------------------------------------------------- +1. StringCchCopyA was completely replaced with a thin wrapper around + StrSafe helper functions StringValidateDest*() and + StringCopyWorkerA_0(), guaranteeing that the user-supplied length + parameter a2 is honoured. +2. Call sites were updated to pass the correct constant buffer size + (0x104 = 260) instead of variable or uninitialised values. +3. The signature of GetSubstituteHKLFromReg() was changed so that the + second parameter is no longer confused with a buffer size. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a local attacker could trigger a stack overflow in +mmcndmgr.dll leading to arbitrary code execution in the context of the +MMC host process (often elevated) or suppress truncation detection to +bypass path-based security checks. This enables a Security Feature +Bypass as reported in CVE-2025-26633 and potentially elevates +privileges if the host process runs with higher rights. + + +Fix Effectiveness +-------------------------------------------------------------------- +By delegating copying to the well-tested StrSafe helpers and by +hard-coding the correct destination size at every call site, the patch +eliminates the possibility of writing beyond the end of the buffer. +Unit analysis shows no remaining code paths that ignore the size +parameter. Therefore the fix is considered complete and effective. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26634_coremessaging.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26634_coremessaging.dll.txt new file mode 100644 index 0000000..79174a9 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26634_coremessaging.dll.txt @@ -0,0 +1,126 @@ +{'file': 'coremessaging.dll', 'change_count': 6, 'date': 1751831558.3559444, 'patch_store_uid': '6f119a53-a1ca-4e9d-8e9f-d31566198ac2', 'kb': 'KB5051987', 'cve': 'CVE-2025-26634', 'confidence': 0.32} +-------------------------------------------------------------------- +CVE-2025-26634 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Core Messaging (coremessaging.dll) – ALPC message assembly +logic (AssembledChunk, AlpcConnection*) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based Buffer Overflow (unchecked pointer arithmetic / +missing size validation) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CoreMessaging uses the AssembledChunk structure to re-assemble a large +ALPC request that was split into several ChunkComponent buffers. When +a new chunk arrives AlpcConnection::LinkChunkedBufferInBatch() calls +AssembledChunk::AddChunkComponent() to copy the header and the fragment +payload into a pre-allocated heap buffer that belongs to the current +AssembledChunk. + +In the original implementation (see snippet below) AddChunkComponent() +performs only two arithmetic checks: + • is_mul_ok( v2 , *(this+0x54) ) – a 64-bit overflow helper + • "v10 < v11" – underflow check +No upper-bound test ensures that + dest = payloadBase + (chunkIndex * chunkSize) +remains inside the allocated AssembledChunk buffer. The function also +trusts the per-chunk payload length *(a2+0x48) without validating it +against the remaining space in the destination buffer. A malicious +client can therefore provide: + • an over-large current-chunk index (WORD at offset +0x50) or + • an inflated DataLength field (WORD in the ALPC _PORT_MESSAGE) or + • an excessive overall-chunk count +so that the computed copy destination (v10) points far beyond the heap +allocation returned by AssembledChunk::Initialize(). The subsequent +memcpy() will overflow the heap, corrupting adjacent objects and +leading to an elevation-of-privilege inside the privileged process +hosting CoreMessaging (e.g. Win32k, Windows Shell, or a system service +using CMBus). + +Additional gaps that allowed the out-of-bounds copy: + • AlpcConnection::ValidateBufferHeader() accepted an arbitrary + MaxChunkCount (WORD 0x52) as long as MaxCount > Index. No limit of + 0x50 enforced for MaxCount when the buffer was *not* already in a + batch (BIT 0x10 cleared). + • LinkChunkedBufferInBatch() forwarded the raw AlpcBuffer directly to + AddChunkComponent() without verifying that the buffer had already + been "chunk-enabled" (WORD 0x50 != 0). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// coremessaging.dll – before patch +v2 = *((unsigned __int16 *)a2 + 40); // current chunk index +... +if (!is_mul_ok(v2, *((unsigned __int16 *)this + 42))) + return STATUS_INVALID; + +v10 = (char *)v11 + v2 * *((unsigned __int16 *)this + 42); // dest +if (v10 < v11) // only + return STATUS_INVALID; // underflow + +memcpy_0(v10, // <-- OOB + (char *)a2 + 88, // src start + *((unsigned int *)a2 + 18) - 8i64); // src size +``` +The memcpy destination (v10) is attacker-controlled; no bound is +checked against the actual size of the AssembledChunk heap buffer. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends crafted ALPC buffer to a CoreMessaging endpoint. +2. AlpcConnection::Callback_HandleReceivedBuffer() + -> Callback_HandleRequest() + -> LinkChunkedBufferInBatch() + -> AssembledChunk::AddChunkComponent() +3. AddChunkComponent() copies payload beyond the allocated heap region + using attacker-supplied size and index fields. + +Attack Vector +-------------------------------------------------------------------- +Any authenticated local user (including a sandboxed AppContainer) can +open the CoreMessaging ALPC port and send a malformed batched request. +No special privileges are required. The overflow occurs inside a +system process (e.g. svchost, explorer, or a UWP broker) and can be +exploited to execute arbitrary code with the host process integrity +level, typically Medium-IL->High-IL or SYSTEM EoP. + +Patch Description +-------------------------------------------------------------------- +The patch replaces AddChunkComponent() with a rewritten version that: + • Accepts the _PORT_MESSAGE header and explicit buffer-size argument. + • Validates DataLength, chunk index, MaxChunkCount, TotalPayloadSize + (this+0x58), and overall CopyLength against the current heap size. + • Uses ULongLongMult() to perform checked multiplications. + • Rejects any request that would exceed 0xA0 bytes of header or the + accumulated payload size (0xFFA0 limit). + • Introduces GetPortMessageForReply() to allocate/calculate safe + header/payload pointers. + • Tightens AlpcConnection::ValidateBufferHeader() so that + MaxChunkCount <= 0x50, ChunkIndex < MaxChunkCount, and blocks + non-null attribute pointers when the hardening feature flag is on. + • Updates LinkChunkedBufferInBatch() to propagate the validated + buffer size instead of the raw AlpcBuffer. + +Security Impact +-------------------------------------------------------------------- +Successful exploitation yields a heap overwrite in a privileged +process, enabling information disclosure, process takeover, or +execution of arbitrary code with elevated privileges. The issue was +publicly assigned CVE-2025-26634 and classified as an Elevation of +Privilege vulnerability. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code performs comprehensive bounds checking and uses safe math +helpers before every copy, eliminating the out-of-bounds condition. +Because all relevant header fields are now validated both when the +buffer is first received and again before every chunk copy, the window +for overflow is effectively closed. Residual risk is limited to +future logic errors behind the feature flag, but the specific overflow +vector described here is fully mitigated. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26634_coremessagingxp.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26634_coremessagingxp.dll.txt new file mode 100644 index 0000000..a4ac4a4 --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26634_coremessagingxp.dll.txt @@ -0,0 +1,128 @@ +{'confidence': 0.15, 'file': 'coremessagingxp.dll', 'cve': 'CVE-2025-26634', 'kb': 'KB5051987', 'patch_store_uid': 'e19b9e54-3f95-43e5-bee8-cd5d47012c93', 'change_count': 1, 'date': 1751831546.933251} +-------------------------------------------------------------------- +CVE-2025-26634 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Core Messaging (coremessagingxp.dll) +Function: Microsoft::CoreUI::Dispatch::UserAdapter:: + ReschedulePendingSendDispatchIfNecessary() + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based Buffer Overflow caused by logic error in state +machine / flag handling + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The UserAdapter object owns an internal flag word located at offset ++0x18 (tested through *(v2+24)). Bit 0x20 denotes that a "send +message" dispatch has already been scheduled. The helper +ReschedulePendingSendDispatchIfNecessary() is supposed to clear that +bit and enqueue a new dispatch **only** when the global mitigation flag +`s_IsMissingSendMessageWorkaroundEnabled` is set. + +Before the fix the code executed in the following order: +1. Test if the queue is in an inconsistent state + (bit 0x20 set *and* queue status does **not** report a pending + send message). +2. Emit a diagnostic via TrackErrorWorker(). +3. If the workaround flag is *disabled* it called FailFast() – + theoretically terminating the process – otherwise execution + continued. +4. Regardless of the result of the FailFast() call the compiler had + already generated instructions that + a) cleared bit 0x20 + b) called ScheduleDispatch(this,12,…) + c) incremented telemetry + +Because FailFast() is a normal __declspec(noreturn) routine that can be +bypassed (e.g. by installing a VEH handler or by fuzzing inside a +sandbox where the exception handler chain is under attacker control), +steps 4a–4c could still be reached with the workaround flag **false**. + +Executing ScheduleDispatch() twice on the same adapter corrupts an +internal heap-allocated message block list. The second call reuses a +pointer that is still linked in the active queue, resulting in a +classic heap-based buffer overflow during the next dequeue cycle. The +overflow occurs inside the Core Messaging heap (user-mode) which is +shared between processes hosting UI stacks, so arbitrary memory can be +overwritten with attacker-controlled message data. + +Affected data structures + • UserAdapter flag word (offset +0x18) + • CM_THREAD_MESSAGE (heap block passed to ScheduleDispatch) + • Global boolean s_IsMissingSendMessageWorkaroundEnabled + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if (!UserAdapter::s_IsMissingSendMessageWorkaroundEnabled) + Cn::FailFast::ForWin32(); // supposed to abort +_InterlockedAnd((int*)(v2+24),0xFFFFFFDF); // clear 0x20 +UserAdapter::ScheduleDispatch(this,12, *((QWORD*)this+4)); +UserAdapter::IncrementSendMessageWorkaroundTelemetry(v5); + +// AFTER +if (UserAdapter::s_IsMissingSendMessageWorkaroundEnabled) +{ + _InterlockedAnd((int*)(v2+24),0xFFFFFFDF); + UserAdapter::ScheduleDispatch(this,12, *((QWORD*)this+4)); + UserAdapter::IncrementSendMessageWorkaroundTelemetry(v5); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Attacker manipulates window messages so that: + 1. Bit 0x20 is set on an adapter (Send scheduled) + 2. The real message is removed, clearing QUEUESTATUS_SENDMESSAGE + 3. Victim thread calls ReschedulePendingSendDispatchIfNecessary() + 4. FailFast() is intercepted / bypassed -> execution continues + 5. ScheduleDispatch() runs twice for the same heap block + 6. Subsequent dequeue overruns heap buffer, gaining code execution + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker (or low-privileged sandboxed code) +crafts a sequence of Win32 SendMessage / PostMessage calls that +force the adapter into the inconsistent state, then exploits an +instrumented VEH/SEH to survive the FailFast() path and trigger the +overflow in a higher-integrity UI or service process. + + +Patch Description +-------------------------------------------------------------------- +The fix simply moves the flag-clearing and dispatch-rescheduling code +inside the `if (s_IsMissingSendMessageWorkaroundEnabled)` block and +removes the FailFast() call entirely. As a result: + • When the workaround is *disabled* nothing further happens – no + state is modified, so the corrupt double-schedule path disappears. + • When the workaround is *enabled* the original behaviour is kept but + is now explicitly gated, eliminating the inconsistent branch. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could achieve an arbitrary heap +overwrite inside a higher-privileged Core Messaging host, leading to +Elevation of Privilege (EoP). Remote exploitation is theoretically +possible via UI-accessible surfaces exposed over the network (e.g. +RemoteApp), matching the vendor classification. + + +Fix Effectiveness +-------------------------------------------------------------------- +The conditional block ensures the vulnerable code is unreachable unless +the global workaround flag is set. Assuming the flag is disabled in +shipping builds, the overflow is fully mitigated. No additional bounds +checks were necessary. Residual risk is limited to configurations +where administrators deliberately enable the workaround registry switch +– "unknown" outside Microsoft. diff --git a/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26645_mstscax.dll.txt b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26645_mstscax.dll.txt new file mode 100644 index 0000000..3123c6c --- /dev/null +++ b/reports/2025-mar-12390-windows_11_24H2_x64/CVE-2025-26645_mstscax.dll.txt @@ -0,0 +1,116 @@ +{'date': 1751866961.4339397, 'patch_store_uid': '3b2f8b19-c99c-42c6-a7dc-5e322f7ba96b', 'file': 'mstscax.dll', 'change_count': 164, 'kb': 'KB5053598', 'cve': 'CVE-2025-26645', 'confidence': 0.18} +-------------------------------------------------------------------- +CVE-2025-26645 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Remote Desktop Client (mstscax.dll) – path sanitisation logic used by +client-side drive / clipboard redirection and file-transfer features. +The affected routines are IsDosDevice(), IsDosDeviceInt(), and the +legacy helper now renamed IsDosDeviceOld(). + +Vulnerability Class +-------------------------------------------------------------------- +Relative Path Traversal / Improper Input Validation (CWE-23, CWE-284). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The client receives file-system paths from the RDP server (for example +when the server asks the client to create or open a file through drive +redirection). Before the patch the client called +IsDosDevice(path) to reject paths that resolve to reserved DOS device +names (CON, AUX, COM1 …) because such names are treated by the Windows +I/O manager as device objects rather than ordinary files. + +Old logic (IsDosDevice): + 1. Search the first backslash in the supplied UNICODE_STRING *a1. + 2. Compare the substring that follows the backslash, case-insensitive, + against a static table SrvDosDevices[0x17] (23 entries). + 3. If a match is found return TRUE, else FALSE. + +Only those 23 names were recognised. Several additional reserved +names (e.g. "CONIN$", "CONOUT$", "LPT9", "COM9", "NUL" variants, etc.) +were missing. When the server supplied a path that contained any of +those unrecognised names the function returned FALSE, signalling to +higher-level code that the path was safe. Subsequent file operations +were therefore executed against an I/O device object rather than an +on-disk file, breaking the intended security boundary. + +Depending on the chosen device name and the operation (read, write, +create), an attacker controlling the RDP server could: + • write arbitrary bytes to a device that maps into the client’s file + system namespace (e.g. overwrite arbitrary files through the NTFS + DOS device mechanism), or + • read sensitive data, or + • cause the client to load / execute uncontrolled content, leading to + remote code execution in the context of the user running mstsc.exe. + +The bug is entirely caused by an incomplete allow/deny list – no bounds +errors or race conditions are involved. Parameters affected: + ‑ a1 : pointer to the wide-string path obtained from the server + ‑ internal const wchar_t **SrvDosDevices (23 names) + ‑ missing: additional 8 reserved names now supplied through + SrvDosDevicesExt. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// OLD +v3 = wcschr(a1, '\\'); +for (i = 0; i < 0x17; ++i) { + if (!wcsicmp(v3 + 1, SrvDosDevices[i])) + return 1; // allowed only 23 names +} +``` +```c +// NEW +if (IsDosDeviceInt(a1, &SrvDosDevices, 0x17) || + (g_bValidateForExtDOSNames && + IsDosDeviceInt(a1, &SrvDosDevicesExt, 8))) + return 1; // covers full superset +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Server sends crafted file path -> Client-side redirection code +calls IsDosDevice() -> Function misses extended device name -> +Caller treats path as ordinary file -> Windows creates/opens device +object -> Attacker gains unintended file / device access. + +Attack Vector +-------------------------------------------------------------------- +A malicious RDP server presents a specially crafted pathname containing +an unrecognised reserved DOS device (e.g. "..\\CONIN$\\foo"). When the +victim user connects with the Windows Remote Desktop Client, the +client performs the requested file operation locally, leading to +arbitrary file write/read or code execution. + +Patch Description +-------------------------------------------------------------------- +1. Introduced helper IsDosDeviceInt() that generically scans an arbitrary + string table. +2. Added a second table SrvDosDevicesExt (8 additional reserved names). +3. Added two globals: + g_bValidateForExtDOSNamesRead – one-time registry toggle reader. + g_bValidateForExtDOSNames – TRUE by default unless + AllowCertainDOSFiles==2 in + HKCU. When TRUE the extended list + is checked. +4. Original IsDosDevice() now calls IsDosDeviceInt() twice (base list + and, conditionally, extended list), closing the gap. + +Security Impact +-------------------------------------------------------------------- +Before the fix a remote, unauthenticated RDP server could exploit the +path validation gap to perform relative path traversal leading to local +file overwrite or execution of arbitrary code on the client machine. +This is classified by Microsoft as Remote Code Execution (RCE). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch fully addresses the root cause by ensuring that *all* known +reserved DOS device names are filtered. The code path is now data- +driven and easier to audit. Administrators can intentionally relax the +check via the documented registry key, but the secure default remains +enabled. No residual bypass is visible in the supplied diffs. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-24063_ks.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-24063_ks.sys.txt new file mode 100644 index 0000000..20ed090 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-24063_ks.sys.txt @@ -0,0 +1,127 @@ +{'kb': 'KB5058411', 'date': 1751754788.523614, 'confidence': 0.11, 'file': 'ks.sys', 'patch_store_uid': 'e0e128b9-e29b-43e3-bc0f-a3d588c7804a', 'cve': 'CVE-2025-24063', 'change_count': 5} +-------------------------------------------------------------------- +CVE-2025-24063 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kernel Streaming (ks.sys) – CKsMdlcache:: +MdlCacheHandleThunkBufferIrp() + +Vulnerability Class +-------------------------------------------------------------------- +CWE-122: Heap-based Buffer Overflow (manifesting through a dangling +pointer/use-after-free that leads to pool memory corruption) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper CKsMdlcache::MdlCacheHandleThunkBufferIrp() prepares an IRP +for zero-copy (MDL-based) data transfer. It walks a user-supplied +“thunk buffer” that consists of one or more 0x38-byte stream headers +and, for every header that has KSSTREAM_HEADER_OPTIONSF_TYPECHANGED +(bit 0x8000) set, it builds an MDL chain and attaches it to +Irp->MdlAddress. + +If any consistency check fails, the routine jumps to an error label +(LABEL_25 in the old build). The error path: + • Iterates over the MDL chain + • Unlocks pages if necessary + • Frees every MDL with IoFreeMdl() + +Crucially, in the original code Irp->MdlAddress is *not* cleared on the +common path. It is only nulled when an obscure, feature-flagged helper +Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_8() is enabled. In +stock configurations the pointer therefore remains non-NULL while +already freed. + +When execution returns to the I/O manager, subsequent code (for +example IoCompleteRequest(), IoFreeIrp() or driver-supplied Cancel/Clean- +up handlers) treats Irp->MdlAddress as valid and will touch, unlock or +free the same memory again. Because pool blocks are reused, the stale +pointer now aliases attacker-controlled data. Typical secondary +operations (MmUnlockPages, IoFreeMdl, memcpy, memset, etc.) will then +read or write past the bounds of the re-allocated object, corrupting +adjacent heap data in kernel space. + +Attacker-controlled layout of the look-aside / NonPagedPool enables +reliable pool-overflow style exploitation and ultimately arbitrary code +execution in kernel mode (local EoP). + +Key data items involved: + IRP → Irp->MdlAddress (dangling) + MDL → _MDL structure allocated from NonPagedPool + Stream header → user buffer that drives the state machine + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old – LABEL_25: free without NULLing +MdlAddress = a2->MdlAddress; +if (MdlAddress) { + do { + Next = MdlAddress->Next; + if (MdlAddress->MdlFlags & 2) + MmUnlockPages(MdlAddress); + IoFreeMdl(MdlAddress); + MdlAddress = Next; + } while (Next); + /* pointer left dangling unless obscure flag 8 is set */ +} +``` + +```c +// New – pointer is always cleared +MdlAddress = a2->MdlAddress; +while (MdlAddress) { + Next = MdlAddress->Next; + if (MdlAddress->MdlFlags & 2) + MmUnlockPages(MdlAddress); + IoFreeMdl(MdlAddress); + MdlAddress = Next; +} +a2->MdlAddress = 0; // <-- unconditional NULL +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens a KS filter pin (CreateFile on ks.sys exposed + device). +2. Sends IRP_MJ_READ / IRP_MJ_DEVICE_CONTROL with a crafted user buffer + that: + • Is at least 0x38 bytes long + • Sets OPTIONSF_TYPECHANGED (0x8000) to steer the MDL path + • Forces an early error (e.g. by omitting the data pointer) +3. CKsMdlcache::MdlCacheHandleThunkBufferIrp() allocates and attaches + one or more MDLs, then hits the error condition and frees them but + forgets to clear Irp->MdlAddress. +4. I/O manager or another driver path later re-uses the dangling + pointer, leading to heap corruption in kernel mode. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user with the ability to send IOCTL/Read requests +to a KS pin (multimedia subsystem). No special privileges are needed +beyond DEVICE_READ/WRITE access. + +Patch Description +-------------------------------------------------------------------- +The update makes two relevant changes: +1. After freeing the MDL list in *all* exit paths, the code now + unconditionally sets Irp->MdlAddress = NULL. +2. Additional refactoring adds Boolean guards (v5/v31) and a feature + flag (DeviceUsage_5) that routes most callers through a rewritten, + safer code path, but the decisive fix is the NULL assignment. + +Security Impact +-------------------------------------------------------------------- +A dangling MDL pointer let attackers induce a second free or cause +kernel routines to operate on attacker-controlled pool data. By +spraying memory, this can be turned into an arbitrary kernel heap +overflow, allowing elevation to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +Clearing the pointer removes the stale reference, converting all later +checks into benign NULL tests and preventing double-free/re-use. No +further data paths reference freed memory, so the patch fully +neutralises the vulnerability. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-27468_securekernel.exe.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-27468_securekernel.exe.txt new file mode 100644 index 0000000..242325f --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-27468_securekernel.exe.txt @@ -0,0 +1,117 @@ +{'kb': 'KB5058411', 'file': 'securekernel.exe', 'change_count': 1, 'date': 1751781007.8450472, 'confidence': 0.19, 'patch_store_uid': 'ec1df4e1-d7ec-4e0f-b057-94ea15e23c66', 'cve': 'CVE-2025-27468'} +-------------------------------------------------------------------- +CVE-2025-27468 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Secure Kernel (securekernel.exe) – routine IumCopyEnclaveMemory +at RVA 0x4FFE0. The helper is responsible for copying data between an +enclave page and a non-enclave page on behalf of kernel callers. + +Vulnerability Class +-------------------------------------------------------------------- +Improper bounds check / out-of-bounds write leading to privileged memory +corruption (CWE-120 style defect, mapped to CWE-269 privilege +escalation). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +IumCopyEnclaveMemory copies at most a page at a time. For every +iteration it derives the number of bytes that may be copied without +crossing the current source page boundary: + + v28 = 4096 - (srcPtr & 0xFFF); + if (v28 > RemainingLen) v28 = RemainingLen; + +The routine then **tries** to ensure that the copy also fits inside the +destination page by testing + + if (v28 + (dstPtr & 0xFFF) > 0x1000) + v28 = 4096 - (srcPtr & 0xFFF); + +This is wrong – when the test is true the code rewrites the length with +the size of the *source* page tail instead of the space left in the +*destination* page. A typical mis-case is: + + src offset = 0 + dst offset = 3500 + Remaining = 600 + +v28 becomes 600, the test triggers (3500+600>4096) and v28 is reset to +4096, so **Size > RemainingLen** and certainly crosses the destination +page that has never been validated by SkmiGetValidEnclaveContainerPte. + +The subsequent + + memmove(dst, src, Size) + +is executed inside Secure-Kernel context with full privilege, causing an +out-of-bounds write into the following page. Because the second page may +belong to arbitrary kernel or secure-kernel data, an enclave process can +corrupt privileged memory and obtain execution at secure-kernel IRQL. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – wrong adjustment +v28 = 4096 - (src & 0xFFF); +if (v28 > remaining) v28 = remaining; +if (v28 + (dst & 0xFFF) > 0x1000) + v28 = 4096 - (src & 0xFFF); // resets to wrong limit +Size = v28; +... +memmove(dst, src, Size); + +// AFTER – fixed computation +v28 = 4096 - (src & 0xFFF); +if (v28 > remaining) v28 = remaining; +unsigned dstOff = dst & 0xFFF; +unsigned limit = 4096 - dstOff; +Size = (dstOff + v28 <= 0x1000) ? v28 : limit; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker invokes a kernel interface that eventually calls + IumCopyEnclaveMemory(src, dst, len, DirectionFlag). +2. Chooses dst so that (dst & 0xFFF) is near 0x1000 and len smaller than + the space to the end of the *source* page but larger than the space + to the end of the destination page. +3. Function mis-computes Size and memmove crosses into an unvalidated + page. +4. Memory corruption in secure kernel occurs; attacker gains arbitrary + write inside secure-kernel address space. + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to allocate an enclave and to request +IumCopyEnclaveMemory (through a documented or undocumented system +service) supplies crafted source/destination addresses that straddle a +page boundary. No special privileges beyond enclave ownership are +needed. + +Patch Description +-------------------------------------------------------------------- +The update rewrites the length calculation: +• Introduces variables v29/v30 (= dstOffset, dstRoom). +• Size is now the minimum of remaining length and **destination** page + room when dstOffset+v28 would overflow. +• Renames bookkeeping variables so the source/destination pointers are + not swapped later in the loop. +• No other logic is changed. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could perform an out-of-bounds write +in secure-kernel memory, escaping the enclave boundary and achieving +arbitrary code execution in Secure Kernel Mode. That directly leads to +full system compromise (Elevation of Privilege). + +Fix Effectiveness +-------------------------------------------------------------------- +The new calculation explicitly limits the copy length to the smaller of +(source room, destination room, remaining length). Therefore the copy +can no longer cross into an unchecked page. No additional vulnerable +paths were observed in the diff; the fix appears comprehensive for this +routine, but similar helpers should be reviewed for the same pattern. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29837_msi.dll.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29837_msi.dll.txt new file mode 100644 index 0000000..400375a --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29837_msi.dll.txt @@ -0,0 +1,126 @@ +{'date': 1751781027.6298983, 'file': 'msi.dll', 'cve': 'CVE-2025-29837', 'patch_store_uid': '53dca6a2-4438-46c8-80db-00ae8ec17e3c', 'confidence': 0.18, 'kb': 'KB5058411', 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-29837 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Installer (msi.dll) – functions CMsiOpExecute::RemoveFile() and +CMsiTable::AllocateData(). Only RemoveFile() is relevant to the +information-disclosure bug fixed under CVE-2025-29837. + +Vulnerability Class +-------------------------------------------------------------------- +Improper link resolution before file access (CWE-59, Link­-following). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The privileged MSI engine (running as SYSTEM) removes files during +un-install by calling CMsiOpExecute::RemoveFile(). When the target +lies inside the product’s backup folder the old code executed the +following sequence: + + 1. IsInBackupFolder() => sets flag = TRUE. + 2. Immediately calls CElevate::CElevate() to obtain full SYSTEM + privileges. + 3. Calls LockdownPath(targetPath, 0) and later CreateFileW() with + GENERIC_READ | FILE_SHARE_READ, **without** validating that + targetPath is on the system drive *or* that it is free of reparse + points / junctions. + +Because CreateFileW is invoked without the FILE_FLAG_OPEN_REPARSE_POINT +flag the API follows reparse points. A local attacker who can create a +junction/symlink under the product’s backup folder can therefore coerce +Windows Installer into opening an arbitrary file elsewhere on the +system. Subsequent code paths (e.g. BackupFile(), VerifyAccessibility +and MoveFileW()) may copy or log that data, resulting in unintended +information disclosure from SYSTEM to the attacker’s account. + +Patch version inserts an optional WIL feature gate +`Feature_Servicing_MSI_GlobalAlloc_buffer_fixes` (id 2578215227) and, +more importantly, a new gate `Feature_2684632377` around additional +validations: + + • Before LockdownPath() the code now calls + CheckSystemDrivePath(path,&flag). If the candidate points outside + the system drive the function logs an error and aborts. + • Additional AIS (Advanced Installer Service) token / flag checks are + performed; if the caller is non-admin and neither AIS nor SYSTEM + applies, the removal is denied. + • The junction-safety CreateFileW probe remains, but is now executed + only after the above validations succeed. + +Fail-fast returns (value 0 / 1) replace the old behaviour, stopping the +flow before any privileged file handle is obtained on an unsafe path. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* BEFORE – no system-drive or junction check */ +if ( (_BYTE)v74 ) +{ + CElevate::CElevate((CElevate *)&v74, 1); + v32 = (...)(v69, 80); // target path + v33 = LockdownPath(v32, 0); + ... // continues with CreateFileW() +} + +/* AFTER – added validation */ +if ( (_BYTE)v81 ) +{ + if (CheckSystemDrivePath(v30, &v81) != 1) { + DebugString(... "ERROR: Check System Drive Path %s unsuccessful"); + goto bailout; + } + if (IsAdmin() || GetAISToken() || GetAISFlag() || (_DWORD)v81==1) + CElevate::CElevate((CElevate *)&v81, 1); + else { + StartImpersonating(); + LockdownPath(...); + StopImpersonating(...); + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker installs a package whose backup folder is writable. +2. Attacker creates a junction inside that folder pointing to a + sensitive file (e.g. C:\Windows\System32\config\SAM). +3. Package uninstall calls CMsiOpExecute::RemoveFile(). +4. Old code elevates and opens the junction, leaking the SAM file’s + contents (via backup or logging). +5. Patch aborts at CheckSystemDrivePath(), preventing the open. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker with write access to a product’s backup +folder (typically the user performing the install) creates a reparse +point to an arbitrary target. Windows Installer running as SYSTEM then +follows the link during RemoveFile(), exposing the target’s data. + +Patch Description +-------------------------------------------------------------------- + • Introduced CheckSystemDrivePath() verification before any privileged + access to a backup-folder file. + • Added AIS token / flag logic to decide whether full elevation is + necessary; otherwise operates under impersonation. + • Wrapped new logic in a WIL feature gate (id 2684632377) for safe + rollout. + • Numerous variable renames/refactors; functional change is confined + to the new validations and early exits. + +Security Impact +-------------------------------------------------------------------- +Before the fix a non-privileged user could cause Windows Installer to +open (and potentially copy or log) arbitrary files with SYSTEM +privilege, resulting in local information disclosure and a breach of +the NTFS security boundary. + +Fix Effectiveness +-------------------------------------------------------------------- +The added CheckSystemDrivePath() plus AIS/admin gating blocks paths that +resolve outside the system drive or through reparse points, removing +the unsafe link-following scenario. Provided the WIL feature flag is +deployed/enabled globally the patch fully mitigates the described +attack. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29839_mrxsmb.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29839_mrxsmb.sys.txt new file mode 100644 index 0000000..f66fd5e --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29839_mrxsmb.sys.txt @@ -0,0 +1,142 @@ +{'change_count': 4, 'patch_store_uid': 'c0e5b152-c728-43e8-bacc-5805c283b07a', 'file': 'mrxsmb.sys', 'kb': 'KB5058411', 'date': 1751781009.9834957, 'confidence': 0.16, 'cve': 'CVE-2025-29839'} +-------------------------------------------------------------------- +CVE-2025-29839 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel FsRtl (File-system Runtime Library) as used by the +Multiple UNC Provider (mrxsmb.sys). Affected routine is +FsRtlValidateChangeNotifyBuffer(), reached by directory-change +notification paths such as NtNotifyChangeDirectoryFile and SMB2 +CHANGE_NOTIFY. + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read / information disclosure (CWE-125). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +FsRtlValidateChangeNotifyBuffer() is tasked with sanity-checking a +caller-supplied buffer that supposedly contains one or more chained +FILE_NOTIFY_INFORMATION structures: + + struct FILE_NOTIFY_INFORMATION { + ULONG NextEntryOffset; // a1[0] + ULONG Action; // a1[1] + ULONG FileNameLength; // a1[2] + WCHAR FileName[...]; + }; + +The routine iterates through the buffer, advancing by +NextEntryOffset (v7) for every record and performing basic range +checks. + +In the vulnerable build the loop looked like this (simplified): + + while (TotalLen >= Offset + 4) { + Next = *a1; // safe (needs 4 bytes) + FileNameLength = a1[2]; // REQUIRES 12 bytes <== BUG + if (Offset + 12 > TotalLen) break; // check too late + ... + } + +The code fetches a1[2] (8 bytes past the start of the current record) +*before* it has verified that there are at least 12 bytes remaining in +the buffer. If the caller supplies a buffer whose tail contains only +4–11 bytes, the read of a1[2] accesses up to 8 bytes beyond the caller +buffer. Because the access happens in kernel mode it can succeed and +will pull arbitrary kernel memory into the caller-controlled buffer. +Subsequent IRP completion copies that buffer back to user space, +allowing disclosure of uninitialized or adjacent kernel data. + +Additional limits (TotalLen < 0x7FFFFFFF, alignment checks, etc.) do +not prevent this under-read. + +The MRxSmbSetInstanceConfig() changes in the same patch are cosmetic +and unrelated to the root cause. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch +while (v3 >= v5 + 4) { // only confirms 4 bytes remain + v7 = *a1; // read NextEntryOffset (4 bytes) + if (v5 + 12 > v3) break; // <-- length check is AFTER access + v8 = a1[2]; // read FileNameLength (requires 12) + ... +} + +// after patch +if (v2 >= 4) { + do { + v6 = *a1; // read 4 bytes (still safe) + if (v5 + 12 > v2) break;// ensure 12 bytes BEFORE next read + v7 = a1[2]; // now guaranteed in-bounds + ... + } while (v2 >= v5 + 4); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode -> kernel I/O manager +NtNotifyChangeDirectoryFile + -> FsRtlNotifyInitializeSync + -> FsRtlNotifyFilterChangeDirectory + -> FsRtlValidateChangeNotifyBuffer (vulnerable) + parses user buffer and may over-read + -> completion routine copies data back to caller + +Exact higher-level path may vary (SMB redirector, third-party FS, +etc.); only the vulnerable routine is certain. + + +Attack Vector +-------------------------------------------------------------------- +A local, unprivileged attacker allocates a page-aligned buffer and +fills it with any value followed by 4–11 valid bytes, then calls +NtNotifyChangeDirectoryFile (or any IOCTL that internally invokes the +FsRtl notify logic) passing that buffer and its reported length. When +the kernel validates the buffer it reads 8 bytes past the end and then +copies the full buffer back to user space, disclosing kernel memory +located immediately after the supplied allocation. + + +Patch Description +-------------------------------------------------------------------- +1. Re-ordered field accesses so that *all* 12-byte header data are only + read after confirming at least 12 bytes remain. +2. Added an explicit outer length check (v2 >= 4) before entering the + loop. +3. Introduced optional call to FsRtlValidateChangeNotifyBufferEx() for + builds where Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() is + enabled, providing a hardened validation path. + +No structural changes were made to the FILE_NOTIFY_INFORMATION parser; +the fix is purely defensive bounds checking. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a malicious caller could force the kernel to read up +to 8 bytes past the end of a user-supplied buffer, and that stale +kernel memory would then be returned to user space. This constitutes +an information disclosure vulnerability that can be chained with other +issues (e.g. KASLR or heap grooming) to facilitate elevation of +privilege exploits. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added length test guarantees that no field beyond the first 4 +bytes is read until the routine has verified at least 12 bytes are +still available, fully eliminating the described out-of-bounds read. +The early outer guard and optional call to the extended validator +provide additional safety nets. No residual paths that bypass these +checks are observable in the patched code, so the fix appears +complete. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29839_mrxsmb20.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29839_mrxsmb20.sys.txt new file mode 100644 index 0000000..4aff527 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29839_mrxsmb20.sys.txt @@ -0,0 +1,136 @@ +{'date': 1751780999.4937491, 'change_count': 2, 'file': 'mrxsmb20.sys', 'cve': 'CVE-2025-29839', 'kb': 'KB5058411', 'confidence': 0.64, 'patch_store_uid': '1b7cdb19-0eb8-4933-85cf-f3206501b6ce'} +-------------------------------------------------------------------- +CVE-2025-29839 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows UNC Provider / SMB2 redirector driver (mrxsmb20.sys) – +client-side processing of SMB2 CHANGE_NOTIFY responses and file ABE +status name-cache handling. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read (information disclosure) – CWE-125. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The buggy path sits in Smb2ChangeNotify_Finalize(). Before the patch +the routine retrieves the buffer-context that holds the raw +FILE_NOTIFY_INFORMATION list returned by the SMB2 CHANGE_NOTIFY +response: + + v9 = *(_QWORD *)(a1 + 160) - 8 ; ctx = BUFFER_CONTEXT + v4 = *(DWORD *)(v9 + 48) ; Status of request + Length = *(DWORD *)(v9 + 300) ; Size of notify payload + +If the I/O status (v4) is success, the code unconditionally trusts the +length field, stores it in RxContext->Information (offset 184), then +continues execution in other layers that iterate through the +FILE_NOTIFY_INFORMATION chain. No attempt is made to verify that + + • the buffer length is non-zero and properly aligned, or + • every NextEntryOffset keeps the walk inside <Buffer + Length>. + +Therefore a malicious SMB2 server can craft a payload whose internal +offsets run past the end of the allocated MDL, causing subsequent +parsers in kernel mode to read from adjacent memory. Because the data +are later copied back to user space (and may even traverse the +network), arbitrary kernel information can be disclosed. + +Patch analysis shows two protection measures: + +1. Microsoft inserted a call to FsRtlValidateChangeNotifyBufferEx(). + The helper walks the list and guarantees that the buffer is + self-consistent and fully contained in the declared length. + +2. When validation fails the code now forces STATUS_INVALID_PARAMETER + (0xC000000D / –1073741629) and still finalises the buffer context, + preventing the earlier dereference. + +The validation is gated behind +Feature_1397499194__private_IsEnabledDeviceUsageNoInline() but the +feature is enabled for all supported builds that contain the patch. + +The second diff (Smb2InsertFileAbeStatusCacheEntry) is unrelated to the +leak; it only simplifies name-cache activation and removes a separate +feature flag. + +Affected structures / parameters + a1 + 160 – list entry that links to SMB2_CSE_BUFFER_CONTEXT + ctx+48 – NTSTATUS result of on-wire command + ctx+264 – pointer to notify buffer + ctx+300 – length of notify buffer (supplied by server) + +Failure to validate ctx+264..ctx+299 against ctx+300 allowed an OOB +read starting at ctx+264 + crafted NextEntryOffset. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old (simplified) +if (v4 >= 0) // success path + v4 = *(DWORD *)(ctx+48); // unchanged +if ((v4 & 0xC0000000) != 0xC0000000) + RxCtx->Information = *(DWORD *)(ctx+300); // trust length +SmbCseFinalizeBufferContext(); // done – no check + +// New core fix +if (v4 >= 0) { + length = *(DWORD *)(ctx+300); + if (length) { + status = FsRtlValidateChangeNotifyBufferEx(&Entries, + NULL, v4, *(QWORD *)(ctx+264), length, + (*(_BYTE *)(a1+2066) & 1)); + if (status < 0) { + v4 = STATUS_INVALID_PARAMETER; // 0xC000000D + goto Finalise; + } + } +} +... +Finalise: +SmbCseFinalizeBufferContext(ctx); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User process -> CreateFile / ReadDirectoryChangesW + -> I/O stack sends SMB2 CHANGE_NOTIFY request +Malicious SMB server returns crafted FILE_NOTIFY_INFORMATION chain +mrxsmb20.sys -> Smb2ChangeNotify_Indication() + -> Smb2ChangeNotify_Finalize() (buggy path) + -> Subsequent path enumeration reads past buffer -> leak + +Attack Vector +-------------------------------------------------------------------- +Any local user that can force the Windows client to access a malicious +SMB share (UNC path, WebDAV over SMB, etc.) can supply the crafted +CHANGE_NOTIFY response. No special privileges are required; the +attacker only needs control over the remote server or the network path. + +Patch Description +-------------------------------------------------------------------- +• Added explicit call to FsRtlValidateChangeNotifyBufferEx() to verify + the integrity of FILE_NOTIFY_INFORMATION buffers. +• On validation failure, forces STATUS_INVALID_PARAMETER and skips + further processing. +• Ensures SmbCseFinalizeBufferContext() is invoked with the correct + context pointer in both success and failure cases. +• Replaced in-line directory cache invalidation with + Smb2InvalidateDirInfoCacheEx() (defence-in-depth, not part of fix). + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could read arbitrary uninitialised kernel +memory that sits after the MDL containing the server-supplied buffer +and have it returned to user mode or over the network, leading to +information disclosure and potential ASLR bypass. + +Fix Effectiveness +-------------------------------------------------------------------- +The new validation covers both buffer length and internal offsets; any +malformed buffer now results in STATUS_INVALID_PARAMETER before the +pointer walk occurs. Because the context is still finalised no other +cleanup paths are skipped. The fix fully addresses the described OOB +read. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29841_mcpmanagementservice.dll.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29841_mcpmanagementservice.dll.txt new file mode 100644 index 0000000..351ddfd --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29841_mcpmanagementservice.dll.txt @@ -0,0 +1,130 @@ +{'change_count': 2, 'patch_store_uid': '5e8145b8-149f-4e94-963a-302ceda9ad09', 'file': 'mcpmanagementservice.dll', 'confidence': 0.42, 'date': 1751781012.2216809, 'kb': 'KB5058411', 'cve': 'CVE-2025-29841'} +-------------------------------------------------------------------- +CVE-2025-29841 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Universal Print Management Service (mcpmanagementservice.dll) +McpOperationHandler::CreateOperationThread and +McpOperationHandler::McpOperationThreadProc + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition leading to CWE-416: Use-After-Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The parameter handed to McpOperationHandler::CreateOperationThread +contains two fields: + DWORD OperationType // *(DWORD*)lpParameter + IUnknown* pOperationObject // *((void**)lpParameter + 1) + +Before the patch the code started the worker thread first and only +inside McpOperationHandler::McpOperationThreadProc performed + + pOperationObject->AddRef(); + +This produced a race window: + 1. Main thread calls CreateThread. + 2. Main thread resumes and may immediately Release() or even free the + whole McpOperationHandlerThreadParams structure. + 3. The new thread has not yet executed AddRef(); when it finally + runs, pOperationObject might already be freed or recycled. + +Dereferencing the dangling pointer inside the thread first to perform +AddRef() and later to execute virtual methods (Execute()) results in a +use-after-free. Because the object is polymorphic, the attacker can +overwrite the vtable pointer and obtain arbitrary code execution in the +service context (LOCAL SYSTEM). + +A second failure path existed when CreateThread itself returned NULL. +No AddRef() had been taken, yet the caller still assumed ownership had +been transferred, so the object leaked. While not an immediate +security issue, the missing Release() shows the same lack of ownership +tracking. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +bool CreateOperationThread(void *lpParameter) { + HANDLE h = CreateThread(..., McpOperationThreadProc, lpParameter,...); + return (((uint64_t)h + 1) & ~1) != 0; +} + +__int64 McpOperationThreadProc(_QWORD *Parameter) { + IUnknown *pObj = (IUnknown*)Parameter[1]; + if (pObj) + pObj->AddRef(); // first touch of pObj (race!) + switch (*(DWORD*)Parameter) { /* ... */ } + pObj->Release(); +} +``` +```c +// After (feature gate omitted for clarity) +char CreateOperationThread(unsigned int *lpParameter) { + IUnknown *pObj = (IUnknown*)lpParameter[1]; + pObj->AddRef(); // ownership taken BEFORE thread start + HANDLE h = CreateThread(...); + if (!h) { + pObj->Release(); // balanced if thread creation failed + return 0; + } + return 1; +} + +__int64 McpOperationThreadProc(_QWORD *Parameter) { + IUnknown *pObj = (IUnknown*)Parameter[1]; + pObj->AddRef(); + pObj->Release(); // neutralise extra ref; keeps balance + /* execute operation */ + pObj->Release(); // final Release on completion +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +McpService::* -> McpOperationHandler::CreateOperationThread + (passes McpOperationHandlerThreadParams) +CreateThread returns, caller continues and may destroy params +Worker thread picks up and dereferences freed pOperationObject => UAF + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker able to interact with Universal Print +Management Service can deliberately race the destruction of the +operation object (or substitute it with crafted memory) after calling a +method that internally invokes CreateOperationThread. When the worker +thread later executes, the service dereferences attacker-controlled +memory, allowing code execution at SYSTEM integrity. + +Patch Description +-------------------------------------------------------------------- +1. Pre-thread AddRef(): the service now increments the reference count + on the operation object before CreateThread, closing the race. +2. Balanced Release(): if CreateThread fails the extra reference is + released immediately. +3. Thread proc has a compensating early Release() so that the reference + count taken in the parent is handed back as soon as the thread owns + the pointer, keeping lifetime balanced. +4. Additional parameter-null validation and telemetry have been added + but are ancillary. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could achieve a reliable use-after-free in +a SYSTEM service, leading to elevation of privilege and possibly +arbitrary code execution in kernel/user mode depending on further +exploitation. Exploitation requires only local access; no additional +privileges are needed beyond the ability to trigger Universal Print +operations. + +Fix Effectiveness +-------------------------------------------------------------------- +The added AddRef()/Release() sequence removes the lifetime gap entirely +and guarantees that the object remains valid for the duration of the +race window. Failure paths are handled and input validation was added. +No residual code paths lacking reference tracking were observed in the +provided diff, indicating the fix is complete for the affected +functions. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29841_printworkflowservice.dll.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29841_printworkflowservice.dll.txt new file mode 100644 index 0000000..95a38d1 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29841_printworkflowservice.dll.txt @@ -0,0 +1,122 @@ +{'confidence': 0.21, 'file': 'printworkflowservice.dll', 'date': 1751781017.058594, 'change_count': 61, 'patch_store_uid': '63bab86f-6596-41af-a752-9d67983f99a7', 'cve': 'CVE-2025-29841', 'kb': 'KB5058411'} +-------------------------------------------------------------------- +CVE-2025-29841 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Universal Print Management Service (printworkflowservice.dll) +Affected routines + • PrintSupportVirtualPrinterSession::RequestPreferredPdlHandleForWrite() + • DuplicateHandleToClient() +The service runs inside the PrintWorkflow host under Local System. + +Vulnerability Class +-------------------------------------------------------------------- +Improper access-control / local privilege-escalation caused by mishandled +handle duplication (closely maps to CWE-362 “race condition” in the MS +bulletin, but the concrete defect is missing validation of pseudo +handles). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The service must pass two internal file handles (this->50 and this->53) +from the privileged workflow process to the calling user process. In +pre-patch code the control flow was split by a feature flag +Feature_2578215227: + + if (FeatureEnabled) + DuplicateHandleToClient(priv_handle,…); + else + OpenProcess(callerPid); + DuplicateHandle(GetCurrentProcess, priv_handle, callerProc,…); + +When the flag was disabled the fallback branch executed a raw +DuplicateHandle() on “priv_handle” *without* any validation. If the +stored value happened to be a pseudo handle (-1, ‑2, etc.) or was +already freed, the call duplicated the service’s own privileged process +handle (pseudo handle ‑1) straight into the unprivileged caller. The +caller thus received a Local System process handle with full access and +could elevate privileges. + +DuplicateHandleToClient() itself contained a guard against pseudo +handles, but that guard was only executed when the same feature flag was +active: + + if (FeatureEnabled && IsPseudoHandle(src)) Throw(E_HANDLE); + +Hence, as long as Feature_2578215227 was OFF (default on affected +builds), the unsafe path was taken and the guard was skipped – a classic +logic flaw introduced by inconsistent gatekeeping. + +Structures / parameters involved + • this+0x190 (index 50) – read-write PDL file handle + • this+0x1A8 (index 53) – preferred PDL handle + • HANDLE a2/a3 – out parameters returned to caller + • DuplicateHandleToClient(HANDLE hSourceHandle,…) + +No additional locking is performed; multiple callers can race the state +of the stored handles, which could also convert the bug into an +in-service use-after-free, explaining Microsoft’s secondary CWE-416 +classification. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// RequestPreferredPdlHandleForWrite – vulnerable branch removed +- if (!FeatureEnabled) { +- HANDLE hCaller = OpenProcess(PROCESS_DUP_HANDLE, 0, callerPid); +- DuplicateHandle(GetCurrentProcess(), this->hPref, hCaller, …); +- } ++ // Always use validated helper ++ DuplicateHandleToClient(this->hPref, a2); +``` + +```c +// DuplicateHandleToClient – validation widened +- if (FeatureEnabled && IsPseudoHandle(src)) Throw(E_HANDLE); ++ if (IsPseudoHandle(hSourceHandle)) Throw(E_HANDLE); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged client calls the WinRT projection that reaches + RequestPreferredPdlHandleForWrite(). +2. Feature_2578215227 is disabled → old fallback path followed. +3. Service reads a pseudo handle value (-1) from its session object + (attacker can race or coerce this beforehand). +4. DuplicateHandle() is invoked, duplicating the service’s own process + handle into the attacker’s process. +5. Attacker now holds a Local System process handle → privilege + escalation. + +Attack Vector +-------------------------------------------------------------------- +Local. Requires the ability to start a Print Support app / Virtual +Printer session, which is allowed to any authenticated user. +No special privileges are needed beyond that. + +Patch Description +-------------------------------------------------------------------- +• Removed the entire fallback code path that manually opened the caller + process and duplicated raw handles. +• RequestPreferredPdlHandleForWrite() now *always* routes through + DuplicateHandleToClient(). +• DuplicateHandleToClient() renamed its first parameter to HANDLE and + performs an unconditional IsPseudoHandle() test, independent of any + feature flag. +• Telemetry IDs updated; no behavioural change. + +Security Impact +-------------------------------------------------------------------- +Before the patch an unprivileged local user could obtain a full-access +handle to the service process (Local System). With that handle the user +can inject code, manipulate memory, or duplicate further privileged +handles, achieving complete local privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The insecure branch has been entirely excised and the helper routine now +rejects all pseudo handles by design, closing the direct privilege +escalation vector. Unless other unchecked handle sources exist, the fix +is effective. No regression paths were observed in the diff. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29955_vmwp.exe.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29955_vmwp.exe.txt new file mode 100644 index 0000000..4b57259 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29955_vmwp.exe.txt @@ -0,0 +1,116 @@ +{'date': 1751781049.6856084, 'file': 'vmwp.exe', 'change_count': 12, 'patch_store_uid': '6f33e6a9-2b4a-4b56-bc30-175ecfc2250c', 'confidence': 0.17, 'cve': 'CVE-2025-29955', 'kb': 'KB5058411'} +-------------------------------------------------------------------- +CVE-2025-29955 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V Worker Process (vmwp.exe) – statistics and metrics +sub-system (classes WorkerStatsPanel / WorkerHvStatsPanel and their +callers ProcessorMetricDevice, VidPartitionManager). + +Vulnerability Class +-------------------------------------------------------------------- +Null-pointer dereference / improper input validation (CWE-20). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each WorkerStatsPanel instance contains an embedded +WorkerHvStatsPanel object that is only present after the hypervisor +stats page has been successfully mapped. The availability of that +sub-object is indicated by internal state queried through the helper +method WorkerStatsPanel::IsAvailable(). + +Prior to the patch several public helper routines assumed the stats +page was always present: + • WorkerStatsPanel::GetRuntimeForAllProcessors() + • VidPartitionManager::GetHvPartitionRetailStats_L1VH_2409() + • ProcessorMetricDevice::CreateProcessorMetric() + +All three functions blindly dereferenced fields that are only valid +when IsAvailable()==true. + +Example – GetRuntimeForAllProcessors(before): + v3 = *((_QWORD*)this + 6); // pointer to WorkerHvStatsPanel + AcquireSRWLockShared( (PSRWLOCK)(v3+80) ); <-- NULL possible + +If the VM is queried very early in its lifetime, or after stats were +unmapped, the ‘v3’ pointer is NULL. The subsequent lock acquisition +or list walk touches address 0x80, raising an access-violation in +vmwp.exe and terminating the worker process hosting the guest VM. + +A malicious local user can reliably reach this state by repeatedly +querying performance/metrics interfaces (WMI, ETW, Hyper-V counters) +while the target VM is starting, resetting, or shutting down, forcing +vmwp.exe to call the vulnerable helpers before the stats page becomes +available. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v3 = *((_QWORD*)this + 6); +AcquireSRWLockShared((PSRWLOCK)(v3 + 80)); // CRASH if v3==0 +... // walks internal lists +``` + +```c +// after +if (WorkerStatsPanel::IsAvailable((WorkerStatsPanel*)this)) +{ + ... // safe use +} +else +{ + *a3 = 0; // or *a2 = {0} +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client API / WMI request + → ProcessorMetricDevice::CreateProcessorMetric() + → WorkerStatsPanel::GetAllVpTotalRunTime() + → WorkerStatsPanel::GetRuntimeForAllProcessors() [vulnerable] + – NULL dereference → unhandled AV → vmwp.exe crash. + +(or) +Client API → VidPartitionManager::GetHvPartitionRetailStats_L1VH_2409() + → direct access to stats page pointer without validation. + +Attack Vector +-------------------------------------------------------------------- +Local attacker on the host (or inside a management VM with Hyper-V +privileges) issues repeated performance/metric queries against a VM +while that VM is in a state where its WorkerStatsPanel is not yet +available. No special privileges beyond the ability to query metrics +are required. Result: vmwp.exe for the target VM crashes, stopping +and resetting the guest – a denial of service. + +Patch Description +-------------------------------------------------------------------- +The fix introduces explicit availability checks: + • New helper WorkerStatsPanel::IsAvailable() is called at the top of +each affected routine. + • When unavailable the functions return zeros and skip all internal +dereferences. + • Where the stats page is available the code now chooses the correct +HvStatsPanel instance through a feature flag and passes the pointer +safely. + • Similar guards were added to ProcessorMetricDevice and +VidPartitionManager. + +Security Impact +-------------------------------------------------------------------- +Before the patch a non-privileged local actor could crash vmwp.exe, +immediately powering off the associated guest VM(s). Repetition could +lead to sustained denial of service of Hyper-V workloads on the host. +No privilege escalation or data corruption was observed. + +Fix Effectiveness +-------------------------------------------------------------------- +The added IsAvailable() gate removes all paths that could dereference a +NULL WorkerHvStatsPanel pointer. Returned default values ensure the +public APIs remain functional even when stats are not yet ready. The +patch fully mitigates the observed crash condition; no remaining +unchecked dereferences were found in the modified code sections. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29970_bfs.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29970_bfs.sys.txt new file mode 100644 index 0000000..0c3f80b --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29970_bfs.sys.txt @@ -0,0 +1,112 @@ +{'file': 'bfs.sys', 'patch_store_uid': '7ce2abc4-66a3-422e-be49-839e17d7c181', 'kb': 'KB5058411', 'date': 1751759662.259331, 'confidence': 0.23, 'cve': 'CVE-2025-29970', 'change_count': 1} +-------------------------------------------------------------------- +CVE-2025-29970 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Brokering File System (bfs.sys) kernel driver – +function BfsCloseStorage, invoked when a storage object belonging to a +BFS instance is torn down. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (dangling pointer dereference on a doubly +linked list head). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +BfsCloseStorage receives a per-storage context pointer P. P[8] +(pointers are 8-byte slots in the context) is the head of a doubly +linked LIST_ENTRY that chains directory objects allocated from +NonPagedPool. Before the patch the routine freed this list with a C- +style for-loop that re-used the list head variable "i" as both the +iterator and the object being freed: + + for (i = (void **)P[8]; ; ExFreePoolWithTag(i, 0)) { ... } + +Execution order in each iteration was: + + 1. Dereference *i to fetch the next entry (v5 = *i). + 2. If the list is not empty, unlink the first real node and free its + embedded buffer(s). + 3. Execute the update expression ExFreePoolWithTag(i,0). + +Step 3 releases the storage occupied by the LIST_ENTRY head itself, +leaving the variable "i" pointing at freed memory. The loop then +returns to its top, immediately dereferences the dangling pointer +(*i) in step 1 of the next iteration. An attacker who can reclaim the +just-freed pool chunk can control the contents of *i, steering the +kernel into arbitrary memory writes during the unlink sequence or into +an arbitrary address dereference, leading to elevation of privilege in +kernel context. + +The bug is purely intra-thread; no race is needed. Triggering only +requires that the list contains at least one element so that the loop +performs more than one iteration. + +Structures/parameters affected: + • P[8] – LIST_ENTRY head of root directory objects. + • The freed entry’s private buffer ( *((PVOID*)v5 + 2) ) is also + freed, but its content can be reused while the driver still trusts + it. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Vulnerable loop (before patch) +for ( i = (void **)P[8]; // i == list head + ; // no condition + ExFreePoolWithTag(i, 0) ) // FREE list head *here* +{ + v5 = *i; // 1st deref of freed memory on + // next iteration + if ( *i == i ) // empty? + break; + ... // unlink + frees +} +``` +```c +// Patched version +BfsCloseRootDirectory(P); // helper safely disposes list +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode operation creating/closing a BFS storage -> + BfsPostCreateOperation / BfsDereferencePolicyEntryEx -> + BfsCloseStorage(P) -> + vulnerable loop frees list head while still in use -> + dangling pointer dereferenced -> memory corruption. + +Attack Vector +-------------------------------------------------------------------- +A local, sandboxed or otherwise low-privileged process that can mount +or open a BFS storage object (e.g., via CreateFile on a brokered path) +causes the driver to eventually call BfsCloseStorage. By ensuring the +root directory list contains multiple entries, the attacker provokes +the use-after-free and places controlled data in the reclaimed pool to +achieve arbitrary kernel write/execute, escalating privileges. + +Patch Description +-------------------------------------------------------------------- +The loop that manually manipulated and freed the LIST_ENTRY head was +removed. A new helper, BfsCloseRootDirectory(P), now disposes of the +root directory list. This helper presumably walks the list without +freeing the head until enumeration finishes, eliminating the dangling +pointer. No other semantic changes were made; variable renaming only. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, local attackers could attain kernel arbitrary +memory write or execute via pool spraying, leading to elevation of +privilege (ring-0). Reliability is high because the dangling pointer +is used deterministically in the same thread context. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable code path has been completely removed; enumeration and +freeing are now encapsulated in BfsCloseRootDirectory. Assuming the +helper is implemented correctly and is not inlined back to the same +pattern, the specific UAF is fully mitigated. No residual reference +to the freed list head remains in BfsCloseStorage. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29971_wtd.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29971_wtd.sys.txt new file mode 100644 index 0000000..8e7361f --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29971_wtd.sys.txt @@ -0,0 +1,111 @@ +{'kb': 'KB5058411', 'change_count': 7, 'patch_store_uid': '15306ebf-302c-4cb5-8e6b-0e0aa4400221', 'cve': 'CVE-2025-29971', 'date': 1751759752.4639626, 'confidence': 0.17, 'file': 'wtd.sys'} +-------------------------------------------------------------------- +CVE-2025-29971 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Web Threat Defense kernel driver (WTD.sys) – stream-callout routine +sub_1C0002CF0 (renamed to sub_1C0002D40 after patch) that handles +FWPS_STREAM_DATA0 indicated by the Windows Filtering Platform (WFP). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-125: Out-of-Bounds Read (kernel-mode) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The function builds an auxiliary parameter block that is passed to a +secondary parser (sub_1C0003EF4). In the vulnerable build the driver +packs the TCP FIN flag (bit 0 of FWPS_STREAM_DATA0::flags) into the low +byte of a *pointer* before passing that pointer to the helper: + + v32 = &v50; // v50 = SIZE_T consumedBytes + LOBYTE(v32) = v30; // overwrite least-significant byte with + // FIN flag (0/1) + sub_1C0003EF4( (DWORD)v32, // corrupted pointer + (DWORD)&v54, … ); + +Because x64 pointers are little-endian, the write to LOBYTE(v32) +alters the actual address value. The helper subsequently dereferences +this pointer expecting a valid SIZE_T, but now points to an arbitrary +(nearly always non-owned) location in kernel address space. Reading +that memory triggers an access violation that crashes the box +(BUGCHECK 0xD1), resulting in a denial of service. + +Key data involved + • &v50 – local SIZE_T that should hold the number of bytes consumed. + • v30 – TCP FIN flag (bit 0 of stream flags). + • sub_1C0003EF4 – downstream routine that dereferences the pointer. + • Corrupted pointer lies outside the current stack frame, producing + an OOB read. + +Boundary condition +The corruption happens whenever the stream contains the FIN flag and +all other guards (buffer < 0x2000 bytes etc.) succeed, therefore it can +be triggered remotely with a single crafted TCP session. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable +v32 = &v50; // correct pointer +LOBYTE(v32) = v30; // corrupt address with FIN bit +v33 = sub_1C0003EF4( // helper will deref bogus ptr + (DWORD)v32, + (unsigned int)&v54, + v53, // payload buffer + v45, // length + a6 + 121, + (__int64)&v50); + +// fixed +v32 = sub_1C0003F74( // new helper, flag passed separately + v30, // FIN flag as its own arg + (__int64 *)&v50, // unmodified pointer + v13, v42, + (_BYTE *)(a6 + 121), + &v48); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote attacker opens a TCP connection to a host running WTD. +2. Attacker sends a short data segment followed immediately by FIN. +3. WFP invokes the Stream layer call-out; sub_1C0002CF0 executes. +4. dataLength != 0, a6 != NULL, buffer < 0x2000 ⇒ vulnerable block + reached. +5. FIN bit is copied into low byte of &v50, corrupting the pointer. +6. sub_1C0003EF4 reads through this pointer –> invalid address. +7. Kernel attempts to read unmapped memory –> bugcheck. + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker sends a crafted TCP stream that forces +WTD to process a FIN-marked segment through the vulnerable routine. +No local privileges are required; the crash occurs in kernel context. + +Patch Description +-------------------------------------------------------------------- +The patch removes the pointer/flag overlay. +• Introduces new helper sub_1C0003F74 whose first argument is the FIN + flag; the pointer to the SIZE_T variable is passed *unchanged*. +• All code that previously used the LOBYTE trick has been deleted. +• Minor refactoring: variable types switched to size_t/unsigned, + telemetry identifiers updated, early-exit logic inverted, but these + are incidental. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a remote attacker could trigger an out-of-bounds +read in kernel space leading to a system crash (denial of service). No +information disclosure or code execution is known, but the crash is +fully reliable. + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected code no longer mutates the pointer; the helper receives a +valid address and cannot read outside the intended stack variable. A +review of the patched binary shows no remaining instances of +LOBYTE(pointer) manipulation, so the fix comprehensively addresses the +identified OOB read vector. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29974_ntoskrnl.exe.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29974_ntoskrnl.exe.txt new file mode 100644 index 0000000..2c255ee --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-29974_ntoskrnl.exe.txt @@ -0,0 +1,108 @@ +{'cve': 'CVE-2025-29974', 'patch_store_uid': '200b84b3-2d89-4157-885c-37e85aa79c41', 'confidence': 0.2, 'kb': 'KB5058411', 'change_count': 3791, 'date': 1751789070.8200648, 'file': 'ntoskrnl.exe'} +-------------------------------------------------------------------- +CVE-2025-29974 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel (ntoskrnl.exe) routine formerly exported as +sub_1408FAD58 and now rebuilt as sub_1408E6D3C. The code is part of +the memory-management helper that validates a user-supplied buffer +before forwarding the request to other memory query helpers. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-191: Integer Underflow / Wraparound leading to +CWE-125: Out-of-bounds Read (information disclosure). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The pre-patch implementation receives three user-controlled values: + BaseAddress – start of a user buffer to be examined + a2 – length of that buffer (unsigned int) + a3 – flag that tells the function to perform the + in-depth MemoryBasicInformation test. + +No upper bound is enforced on a2. The only range check is: + + if (BaseAddress + a2 > RegionBase + RegionSize) + FAIL; + +Because the addition occurs before the comparison, a2 values larger +than (2^64 – BaseAddress) cause a 64-bit wraparound. After the +wrap, the resulting pointer is smaller than RegionBase and therefore +passes the check. When the function later copies data from the +validated area (not shown in the diff but required by the higher +level caller), it will read past the end of the real allocation and +return kernel memory to user mode. + +Even without an actual wraparound, supplying an overly large a2 lets +an attacker extend the permitted read beyond the MEM_COMMIT range +returned by ZwQueryVirtualMemory because the code fails to compare +against MEM_RELEASE/MEM_RESERVE except for the single DWORD1(v13) == +32 test. Integer wrapping therefore converts the logical check into +an underflow that opens an arbitrary sized window on kernel address +space. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch – no size limit and susceptible to wraparound +v3 = a2; // attacker-supplied length +... +if ((unsigned __int64)BaseAddress < v11 || + (unsigned __int64)BaseAddress + v3 > *((_QWORD *)&v12 + 1) + v11) + return STATUS_INVALID_PARAMETER; // supposed guard +``` +```c +// post-patch – first line in function +if (a2 > 0x10000) // hard cap at 64 KiB +{ + v8 = STATUS_INVALID_PARAMETER; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User calls the IOCTL or internal API that eventually invokes + sub_1408FAD58 with arbitrary BaseAddress and a2. +2. Function queries VAD information via ZwQueryVirtualMemory. +3. Length check adds a2 to BaseAddress; if the math wraps, the value + is now lower than the region end and validation succeeds. +4. Subsequent helper (not shown) copies (a2) bytes from kernel memory + to user space, disclosing uninitialized or sensitive data. + +Attack Vector +-------------------------------------------------------------------- +An unprivileged local attacker opens a handle to the affected device +or subsystem and requests memory validation with a2 set to a very +large length (>= 2^63). No special privileges are required beyond +local code execution. + +Patch Description +-------------------------------------------------------------------- +Microsoft rewrote the helper: +1. Introduced an explicit length ceiling: if (a2 > 0x10000) fail. +2. Restructured the code to use guarded sections and push-locks; all + address arithmetic is now done after the size is known to be + small, eliminating 64-bit wrap risk. +3. Added additional bookkeeping (array walk, ref-counts) ensuring the + returned pointer is one of the tracked regions before any data is + exposed. + +Security Impact +-------------------------------------------------------------------- +Before the fix, a local attacker could read up to 2^64 bytes minus one +page of arbitrary kernel memory, bypassing KASLR and leaking secrets +such as credential material or code pointers. The flaw does not +provide write primitives but constitutes a high-quality information +leak that can be chained with other bugs for full elevation. + +Fix Effectiveness +-------------------------------------------------------------------- +Capping a2 to 64 KiB removes the possibility of 64-bit wraparound and +limits any accidental over-read to a single, benign page. The +additional consistency checks and locked list membership tests make +it highly unlikely that an untracked region can be validated. Given +the diff, the patch appears complete and effective against the +original underflow vector. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30385_clfs.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30385_clfs.sys.txt new file mode 100644 index 0000000..5b47d20 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30385_clfs.sys.txt @@ -0,0 +1,121 @@ +{'date': 1751781063.067751, 'confidence': 0.11, 'change_count': 34, 'cve': 'CVE-2025-30385', 'patch_store_uid': '50b75c18-3545-4611-ba2f-9638129745a0', 'kb': 'KB5058411', 'file': 'clfs.sys'} +-------------------------------------------------------------------- +CVE-2025-30385 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +clfs.sys – Common Log File System (CLFS) kernel-mode driver. Affected +objects are CClfsLogCcb (per-handle control block) and associated +lifetime-listener infrastructure. + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Dangling Pointer (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each open handle to a CLFS log file owns a CClfsLogCcb structure. A +pointer to that CCB is stored in FILE_OBJECT->FsContext2 and is shared +by multiple asynchronous paths (read/write/flush, archival, restart +I/O, etc.). Prior to the patch the following sequence was possible: + +1. Thread A initiates handle teardown. In + CClfsLogCcb::~CClfsLogCcb() the code tested the “linked” flag + 0x80, unlinked the CCB from the global list and immediately + dropped the last reference (ObfDereferenceObject) **without** first + serialising against racing users. +2. Thread B, already inside one of many request helpers + (ReadLogBlock, ReadRestart, Flush, AdvanceLogBase, etc.) still + held the stale pointer stored in the IRP/CCB and accessed object + fields (e.g. Flags, Archive counters, ILifetimeListener pointer) + after the memory had been freed by Thread A. +3. Because the freed pool is attacker-controlled, subsequent field + dereferences or bit operations cause arbitrary kernel memory reads + or writes, allowing local elevation of privilege. + +Contributing factors +• No acquisition of the CCB’s per-handle ERESOURCE at offset 0x98 + ("+152" in the diff) when clearing flags, unlinking, or touching the + lifetime listener. +• Missing reference counting for ILifetimeListener objects + (pointer at CCB+0x100 / field index 32). +• Flag 0x80 (“linked”) was cleared with a plain AND, not atomically. + +Patch behaviour (core changes) +• CClfsLogCcb::~CClfsLogCcb() now: + – Acquires the handle resource before calling Unlink(). + – Uses InterlockedAnd() to clear 0x80. +• CClfsLogCcb::Unlink() changed the list-manipulation sanity checks + and also uses InterlockedAnd() for the flag. +• New resource acquisition paths were added to + UninstallLifetimeListener(), Add/ReleaseArchiveRef(), Cleanup(), + Request Read/Write/Flush helpers, etc., protecting all updates to + shared CCB state. +• CClfsLogCcb::UninstallLifetimeListener() now validates/clears the + pointer while the same resource is held, preventing a second thread + from observing a half-initialised listener or from re-using the CCB + after it was freed. +• CClfsManagedLogClientUser::Initialize() now installs the lifetime + listener through CClfsLogCcb::InstallLifetimeListener(), which + bumps the CCB reference count. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – UninstallLifetimeListener, no locking, straight free +if ( this[32] != a2 ) return STATUS_INVALID_PARAMETER; +this[32] = NULL; +(*a2->vtable->Release)(a2); // UAF window +``` +```c +// after – lock then validate, pointer cleared while held +ExAcquireResourceExclusiveLite(&this->Res, TRUE); +if (!(this->Flags & CCB_STATE_CLOSED) && this->Listener == a2) { + this->Listener = NULL; +} +ExReleaseResourceLite(&this->Res); +(*a2->Release)(a2); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Open file -> CLFS returns handle and CClfsLogCcb. +2. Issue asynchronous IRP that keeps a pointer to CCB. +3. Close handle from another thread –> object is unlinked and freed. +4. First IRP completes and accesses freed CCB (e.g. marks + flag 0x08, dereferences Listener) –> kernel UAF. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Any user able to open a CLFS log file +and trigger concurrent I/O can race close vs. read/write to gain +arbitrary-kernel-write and elevate privileges. + +Patch Description +-------------------------------------------------------------------- +• Systematically acquires the CCB ERESOURCE around every mutation or + query of shared members (flags, counters, Listener). +• Adds atomic flag manipulation (InterlockedAnd) for 0x80. +• Introduces CClfsLogCcb::InstallLifetimeListener() and uses reference + counting for ILifetimeListener objects. +• Destructor now performs Unlink() only while the resource is held and + after asserting that no outstanding references remain. +• Many request paths (ReadLog*, Flush, AdvanceLogBase, etc.) call + CClfsLogCcb::MarkAccessed() instead of directly setting Flags, again + under protection of the lock or a retained reference. + +Security Impact +-------------------------------------------------------------------- +Before patch: race allowed reuse of freed CClfsLogCcb memory leading to +arbitrary kernel read/write and Local Privilege Escalation (EoP). +After patch: CCB lifetime is reference-counted and protected by an +exclusive resource; concurrent access now sees either a valid object +or NULL, eliminating the UAF. + +Fix Effectiveness +-------------------------------------------------------------------- +The added locking and reference-counting remove the window where a +pointer to freed memory can be dereferenced. No remaining unlocked +paths to CClfsLogCcb fields were observed in the patched diff. The +fix is therefore assessed as effective. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30388_win32kbase.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30388_win32kbase.sys.txt new file mode 100644 index 0000000..63571ed --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30388_win32kbase.sys.txt @@ -0,0 +1,119 @@ +{'change_count': 264, 'confidence': 0.23, 'patch_store_uid': '1556b61b-6e12-44f3-988f-2e5fbc78c3dd', 'file': 'win32kbase.sys', 'cve': 'CVE-2025-30388', 'date': 1751781048.5130446, 'kb': 'KB5058411'} +-------------------------------------------------------------------- +CVE-2025-30388 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kbase.sys (Windows Graphics / GDI kernel driver) +Vulnerable routine: GdiHandleManager::AcquireEntryIndex() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow – CWE-122 + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GdiHandleManager::AcquireEntryIndex() is responsible for handing out a +free GDI handle index from a per-session, non-paged handle table +hierarchy. The function first walks the hierarchy to locate or create +a "sub-table" that still contains free entries, then calculates a +pointer to the chosen 24-byte GDI_HANDLE_ENTRY structure: + + v15 = *(_QWORD *)v14 + 24 * v12; + +v12 is derived from the 16-bit low word of the final handle value +(v7). The only boundary check applied is + + if ((unsigned int)v12 < *(DWORD *)(v14 + 0x14)) + +but when the test fails v15 is deliberately left at 0. A later block +executes unconditionally once the outer loop terminates: + + *(BYTE *)(v15 + 0x0D) = 0; // or + *(BYTE *)(v15 + 0x0D) = WORD1(v7); + +Because the code does not re-validate that v15 is non-NULL (and in the +non-NULL case does not verify that v12 is within the allocated +sub-table), two distinct uncontrolled writes are possible: + +1. v15 == 0 -> write to NULL page (no longer user-mappable on + modern Windows; exploitable on older builds with prior + NULL-dereference barriers) +2. v12 >= Capacity -> v15 points past the end of the allocated + sub-table; the byte write at offset +0x0D lands in adjacent kernel + heap memory, corrupting pool metadata or a neighbouring object. + +Both conditions are fully attacker-controlled through user-mode GDI +API calls that repeatedly exhaust handle tables, forcing the +vulnerable fallback paths inside AcquireEntryIndex(). Once pool +metadata is corrupted, a local attacker can craft subsequent +allocations to obtain execution control in kernel context – turning +the bug into a full Remote/Local Code Execution primitive. + +Affected data structures / fields + * GDI_HANDLE_HEADER (24-byte table element) + * Per-session BaseGlobals::HandleIndexArray (256-KB, tag 'Ghmc') + * Fields at offsets 0x08 / 0x0C / 0x10 of each sub-table header + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – unchecked pointer arithmetic & write +v15 = 0i64; +if ((unsigned int)v12 < *(DWORD *)(v14 + 0x14)) + v15 = *(_QWORD *)v14 + 24 * v12; // <= may run past buffer +... +*(BYTE *)(v15 + 0x0D) = 0; // overwrite out-of-bounds +``` +```c +// after patch – vulnerable function removed, new safe init path +v2 = (BaseGlobalsNonPaged *) + Win32AllocPoolImpl(0x40ui64, 0x788ui64, 'Gesm'); +if (v2) + BaseGlobalsNonPaged::BaseGlobalsNonPaged(v2); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode code + -> GDI API loop (e.g., CreateBitmap) exhausts per-table quota + -> kernel transitions to win32kbase!GdiHandleManager::AcquireEntryIndex + -> path where all existing sub-tables are full is taken + -> new sub-table allocation fails or v12 is outside size + -> boundary check fails, v15 remains NULL / out-of-range + -> *(BYTE *)(v15+0x0D) write corrupts kernel heap + +Attack Vector +-------------------------------------------------------------------- +Local attacker running in a low-privilege desktop session issues +thousands of GDI object creation calls, manipulating the internal free +list until AcquireEntryIndex() executes the unsafe path. No special +privileges are required; the bug is reachable from a sandbox or +browser renderer process. + +Patch Description +-------------------------------------------------------------------- +Microsoft entirely removed the legacy AcquireEntryIndex() code path and +re-architected handle-table initialisation. The replacement routine +(Gre::Base::InitNonPagedGlobals) allocates a fixed-size (0x788-byte) +non-paged structure and calls a constructor that no longer performs +pointer arithmetic based on attacker controlled indices. All previous +calculations of 24*index and byte writes at +0x0D are gone, thereby +eliminating the overflow primitive. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a user-mode process could corrupt non-paged pool and +achieve arbitrary code execution in kernel mode, leading to full +system compromise and sandbox escapes. The issue is rated Remote Code +Execution because many graphics attack surfaces (e.g., RDP, Hyper-V +vGPU) can reach the vulnerable code from outside the victim session. + +Fix Effectiveness +-------------------------------------------------------------------- +Diff analysis shows the vulnerable function has been removed; the new +initialisation routine performs constant-size allocations and does not +use attacker-controlled indices for pointer or size calculations. No +path writes beyond the bounds of the newly allocated buffer. The +specific overflow primitives are therefore completely neutralised. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30388_win32kfull.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30388_win32kfull.sys.txt new file mode 100644 index 0000000..d1f3dd2 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-30388_win32kfull.sys.txt @@ -0,0 +1,110 @@ +{'date': 1751781042.0378962, 'patch_store_uid': '62dac484-f3bc-4d07-9dfd-85240e9d509e', 'confidence': 0.19, 'cve': 'CVE-2025-30388', 'change_count': 158, 'kb': 'KB5058411', 'file': 'win32kfull.sys'} +-------------------------------------------------------------------- +CVE-2025-30388 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel graphics subsystem (win32kfull.sys) – DrawStream +command parser executed through the NtGdiDrawStream system call. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow – CWE-122. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GreDrawStream<XDCOBJ_NoCtor,0>() is the in-kernel dispatcher that +parses a DRAWSTREAM byte-buffer supplied from user-mode and issues +drawing primitives on behalf of the caller. + +Each record in the buffer begins with a 32-bit opcode followed by an +opcode-specific payload whose length is also taken from the stream +itself. While walking the buffer the original code only verified that +at least four bytes (the opcode) remained: + + if (v15 < 4) { v41 = 1; goto exit; } + +Once the opcode had been read the implementation immediately deref- +erenced further fields (e.g. dimensions, colour key, rectangles) and +advanced the stream pointer without first checking that the remaining +buffer length (v15) was large enough for the full record. For example +for opcode 9 (transparent-blt) the code assumed 0x3C bytes were +authorized and blindly accessed them: + + if (*v14 != 9) goto exit; + if (v15 < 0x3C) goto exit; // <-- missing + ... use v14[13] ... + +If the attacker provides a buffer where v15 < 0x3C, v14[13] and other +fields point outside the allocated region. Subsequent calls to +NtGdiDrawStreamInternal() copy those out-of-bounds bytes into heap +buffers located inside win32k, overflowing the heap and giving the +attacker controlled data that is later interpreted as XLATEOBJ, +EXLATEOBJ or palette structures. Because win32k operates in kernel +context, successful exploitation leads to arbitrary kernel code +execution. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – no size validation for opcode 9 +if (*v14 != 9) + goto EXIT; +LODWORD(v36) = 60; // expected record size +if (v15 < 0x3C) // <-- missing check (patched code adds) + goto EXIT; +... // uses v14[1..14] unconditionally +``` +```c +// after – record size is validated before use +if (*v14 != 9) + goto EXIT; +LODWORD(v36) = 60; +if (v15 < 0x3C) + goto EXIT; // newly added +... +``` +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls NtGdiDrawStream(hdc, len, userBuffer). +2. Kernel enters GreDrawStream<XDCOBJ_NoCtor,0>(). +3. Crafted stream sets opcode = 9 and length < 0x3C. +4. Original code accesses v14[13] past end of userBuffer. +5. NtGdiDrawStreamInternal() receives pointers to attacker-controlled + memory living beyond the allocated buffer and copies it into heap + objects ⇒ overflow. + +Attack Vector +-------------------------------------------------------------------- +Any sandboxed or low-integrity process can invoke NtGdiDrawStream with +a malicious buffer. Remote attackers can reach the primitive via RDP +or any vector that allows GDI calls (e.g. malicious document rendered +in a browser sandbox). + +Patch Description +-------------------------------------------------------------------- +Microsoft rewrote the record parser: +* Added per-opcode minimum-size checks (v15 < requiredSize ⇒ abort). +* Restructured control flow to centralised error handling (LABEL_80 / + LABEL_81) ensuring the parser exits on any size mismatch. +* Introduced XDCOBJ::vUnlockFast() paths to correctly balance object + references, eliminating associated UAF side effects. +* Refactored parameter types and renamed variables for clarity but the + functional security fix is the added length validation. + +Security Impact +-------------------------------------------------------------------- +Without the fix, a local or remote attacker can overflow heap buffers +inside win32k, corrupting adjacent pool metadata and achieving +arbitrary code execution in the Windows kernel (RCE / LPE). Successful +exploitation compromises the entire operating system. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched function aborts parsing as soon as the remaining byte count +is smaller than the opcode-specific payload length, so no out-of-bounds +reads or writes are possible. With the added clean-up paths the object +reference counts are also correctly balanced. No further overflow +conditions were observed in the updated code, making the fix +comprehensive for the reported issue. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32706_clfs.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32706_clfs.sys.txt new file mode 100644 index 0000000..fbbbf17 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32706_clfs.sys.txt @@ -0,0 +1,130 @@ +{'file': 'clfs.sys', 'date': 1751781009.636464, 'kb': 'KB5058411', 'cve': 'CVE-2025-32706', 'confidence': 0.26, 'change_count': 34, 'patch_store_uid': '50b75c18-3545-4611-ba2f-9638129745a0'} +-------------------------------------------------------------------- +CVE-2025-32706 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Common Log File System (CLFS) kernel driver +clfs.sys – routine +CClfsBaseFilePersisted::ExtendMetadataBlockDescriptor() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-memory double free / pool corruption due to improper state +validation (CWE-415, manifests as CWE-20 – improper input validation) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The CLFS routine ExtendMetadataBlockDescriptor() is responsible for +increasing the size of one of the three in-memory metadata blocks that +back a CLFS log. Every block is referenced through a descriptor array +located at this->m_pMetadataDesc ("*(this+0x30)"). Each descriptor is +24 bytes: {PVOID pBlock; ULONG cbBlock; ULONG Flags}. + +1. When the incoming request indicates that block i must be replaced by +the contents of block i+1 (the "shadow block" case), the function first +retrieves the pointer stored in descriptor[i] (variable v6) and, if it +is different from the pointer stored in descriptor[i+1], frees it: + ExFreePoolWithTag(v6); + The descriptor entry is then overwritten with the content of +descriptor[i+1]. + +2. Before returning, the routine tries to free the *old* buffer once +again unless that buffer is still marked as a shadow block: + if (v6 && !IsShadowBlock((CClfsBaseFilePersisted*)v21, i, i-1)) + ExFreePoolWithTag(v6); + +3. The intention is that v21 should hold the address of the descriptor +array so that IsShadowBlock() can inspect the current pointer value in + descriptor[i]. Unfortunately, the same register-backed local variable +(v21) is reused for unrelated arithmetic later in the function +(modulus, alignment, etc.). By the time the epilogue executes, v21 no +longer contains a valid pointer – it holds a small integer. +IsShadowBlock therefore operates on an invalid address, always returns +FALSE, and the already-freed buffer referenced by v6 is freed a second +time. + +4. A local attacker that can reach this code path (e.g. via the public +CLFS APIs NtCreateLogFile/NtSetInformationFile → SetEndOfLog → +ExtendMetadataBlockDescriptor) controls the lifetime of the metadata +blocks and can reliably trigger the double free. Subsequent pool +allocations allow re-occupation of the freed chunk, leading to pool +corruption and ultimately arbitrary kernel-mode code execution. + +Affected parameters/structures + • a2 – index of the metadata block (0-2) that is being extended. + • Descriptor array *(this+0x30) – 24-byte records per block. + • Local variables v6 (old buffer) and v21 (descriptor pointer) + whose lifetime overlap incorrectly. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch (epilogue) +```c +if ( v6 && + !CClfsBaseFilePersisted::IsShadowBlock((CClfsBaseFilePersisted *)v21, + v3, v3-1) ) +{ + ExFreePoolWithTag(v6, 0); // second free – corruption +} +``` +After patch +```c +if ( v6 && + !CClfsBaseFilePersisted::IsShadowBlock((CClfsBaseFilePersisted *)v20, + v3, v3-1) ) +{ + ExFreePoolWithTag(v6, 0); // executes only when safe +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode ⇒ NtSetInformationFile(SetEndOfLog) ⇒ + CClfsLogFcbPhysical::SetEndOfLog ⇒ + CClfsBaseFilePersisted::ExtendMetadataBlock() ⇒ + CClfsBaseFilePersisted::ProcessCurrentBlockForExtend() ⇒ + CClfsBaseFilePersisted::ExtendMetadataBlockDescriptor(index,size) + ↳ shadow-block branch followed by function epilogue + ↳ double free on same pool chunk + +Attack Vector +-------------------------------------------------------------------- +A local, low-privileged attacker opens or creates a CLFS log file and +issues crafted SetEndOfLog/CLFS_RECORD_TYPE operations so that: + • block i is a shadow of block i+1, and + • the requested extension size triggers the early reuse branch. +The attacker then performs heap grooming in the kernel pool between the +first and second free to reclaim the chunk with controlled data, +leading to arbitrary kernel write and elevation of privilege. +No special privileges are required beyond the ability to create and +write to a CLFS log. + +Patch Description +-------------------------------------------------------------------- +The update performs defensive refactoring: + • Introduces dedicated variables (v20, v22) so the pointer to the + descriptor array is preserved throughout the function lifetime. + • Replaces the IsShadowBlock() epilogue argument from the clobbered + variable v21 to the preserved pointer v20, preventing an invalid + memory reference and ensuring the second free is executed only when + the buffer is not already freed. + • Minor cosmetic changes (layout, trace GUIDs, type corrections) that + do not affect security. + +Security Impact +-------------------------------------------------------------------- +Before the patch the routine could free the same pool allocation twice. +By reclaiming the freed chunk, an attacker gains the ability to corrupt +pool metadata or object contents, leading to arbitrary kernel memory +write and full local elevation of privilege (ring-0 code execution). +System integrity and confidentiality are fully compromised. + +Fix Effectiveness +-------------------------------------------------------------------- +By ensuring IsShadowBlock() receives a valid, unaltered pointer, the +second free is skipped for buffers already released, eliminating the +double-free condition. No other variable aliasing capable of reviving +the bug is observable in the new code, so the fix is considered +effective for this specific flaw. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32709_afd.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32709_afd.sys.txt new file mode 100644 index 0000000..bc9108c --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32709_afd.sys.txt @@ -0,0 +1,124 @@ +{'confidence': 0.19, 'change_count': 3, 'cve': 'CVE-2025-32709', 'kb': 'KB5058411', 'patch_store_uid': '5dd3b557-7452-4b29-9752-52189a241097', 'file': 'afd.sys', 'date': 1751781051.2551763} +-------------------------------------------------------------------- +CVE-2025-32709 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys). The issue is +in the kernel-mode IOCTL handlers AfdSuperAccept (IOCTL_AFD_SUPER_ACCEPT) +and AfdSuperConnect (IOCTL_AFD_SUPER_CONNECT). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (concurrent access to connection / endpoint +objects that have already been returned to their look-aside list). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AfdSuperAccept and AfdSuperConnect both manipulate AFD_CONNECTION and +AFD_ENDPOINT structures that are reference–counted through + ENDPOINT->ReferenceCount (offset +0x38) + ENDPOINT->State (offset +0x168, bitfield, 0==idle) + ENDPOINT->BusyFlag *(offset +0x168+0x1c) == 4 while + a “super” call is in progress +and are linked through: + ENDPOINT->FreeConnectionSList (offset +0xA0) + ENDPOINT->OutstandingIrpList (offset +0x60) + +In the pre-patch code a connection entry is removed from the free list, +passed to AfdServiceSuperAccept / AfdTdi* helpers and, when these +helpers fail, immediately re-inserted into the list *after the spinlock +has been released* and *without having taken a reference*. The same +pointer (v23 / FreeConnection) is meanwhile still accessible to other +threads, allowing the object to be freed and reused while the current +thread continues to touch its fields (re-insert, queue IRP, complete +IRP, etc.). The identical pattern exists in AfdSuperConnect when it +constructs a connection, queues IRP completion callbacks, and then +invokes the transport-layer dispatch routine without retaining a safe +reference. + +Race window +1. Thread A dequeues a connection from FreeConnectionSList. +2. Thread A releases the socket spinlock and invokes + AfdServiceSuperAccept(), which fails. +3. Before Thread A re-queues the connection, Thread B wins the race + (e.g. Cancel IRP / close socket) and frees the object back to the + AFD look-aside list. +4. Thread A now operates on freed memory, dereferences embedded list + pointers, and ultimately allows an attacker-controlled write/exec in + kernel context. + +Trigger pre-conditions +• (*ENDPOINT->State & 0x400000) == 0 (direct-accept path) +• DepthSList() > 0x7FFF OR AfdGetFreeConnection() != 0 +• Failure in AfdServiceSuperAccept() or transport dispatch path. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (AfdSuperAccept, simplified) +FreeConnection = AfdGetFreeConnection(ep); +... +if (!AfdServiceSuperAccept(ep, FreeConnection, &Lock, ListHead)) { + // < no reference held, spinlock already dropped > + *FreeConnection = ListHead->Flink; // UAF when other CPU freed it + ... // re-insert into list +} +``` + +```c +// post-patch +// – holds rundown protection while list is walked +// – keeps spinlock across error-path reinsertion +// – adds AfdNotifySockIndicateEventsUnlock(), removes window +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User –> NtDeviceIoControlFile( socket, IOCTL_AFD_SUPER_ACCEPT / _CONNECT ) + –> afd.sys::AfdDispatchDeviceControl() + –> AfdSuperAccept() / AfdSuperConnect() + –> race in error path –> freed connection reused –> kernel crash or + controlled write / EoP. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Any user that can create a Winsock +socket (AF_INET/AF_INET6) can send crafted IOCTLs and use thread +cancellation / handle-close races to provoke the UAF and execute code in +kernel mode, thereby escalating privileges. + +Patch Description +-------------------------------------------------------------------- +1. Added explicit feature/state gating: + if (Feature_IsEnabled && (Endpoint->Flags & AFD_FLAG_XXX)) +   return STATUS_INVALID_PARAMETER; +2. Introduced auxiliary pointer (v48) so the original referenced + FileObject is retained for clean-up even if local variable is + overwritten. +3. Took an additional reference / rundown protection on endpoint before + manipulating lists (ExAcquireRundownProtection, AfdRefTLBaseEndpoint) + and releases it in all exit paths. +4. Holds socket spinlock while reinserting failed connection, thereby + eliminating the race window. +5. Uses new helper AfdNotifySockIndicateEventsUnlock() that releases the + spinlock *after* notification has completed. +6. Added consistent error-path that dereferences objects, unlocks MDLs + and completes IRP exactly once. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a local attacker could reliably cause a kernel use- +after-free leading to arbitrary code execution in kernel context and +full privilege escalation (SYSTEM). The race is in socket-level code +reachable from user mode without special privileges. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the dangerous window by (a) holding references/rundown +protection, (b) not releasing the spinlock until the object is safely +re-linked, and (c) rejecting invalid endpoint types up-front. All exit +paths now balance references, making the stale-pointer scenario +unreachable. No residual UAF condition was observed in the patched +handlers. diff --git a/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32709_ndis.sys.txt b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32709_ndis.sys.txt new file mode 100644 index 0000000..dc2b0e1 --- /dev/null +++ b/reports/2025-may-12390-windows_11_24H2_x64/CVE-2025-32709_ndis.sys.txt @@ -0,0 +1,152 @@ +{'file': 'ndis.sys', 'kb': 'KB5058411', 'cve': 'CVE-2025-32709', 'date': 1751781061.094339, 'confidence': 0.33, 'patch_store_uid': '58f43ca8-b3c2-47e5-ab47-dc07c9b90bad', 'change_count': 7} +-------------------------------------------------------------------- +CVE-2025-32709 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows network stack – NDIS (ndis.sys) compartment +management code that backs the Ancillary Function Driver for +WinSock (AFD) and the Network-Store-Interface (NSI) user APIs. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (dangling pointer inside asynchronous +notification path). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Every network compartment is represented by an +NDIS_IF_COMPARTMENT_BLOCK structure that is reference-counted and +kept in a global doubly-linked list. Deleting a compartment is +performed by ndisIfDeleteCompartment(); creation is handled by +ndisIfCreateCompartment()/ndisIfCreateCompartmentBlock(). + +OLD BEHAVIOUR +1. While still holding the global WPP_MAIN_CB.DeviceObjectExtension + spin-lock, the delete routine marked the object as “pending + delete” by setting bit 2 in field Flags (offset +0x28): + Flags |= 2 +2. It immediately queued an asynchronous NSI worker by calling + ndisNsiScheduleCompartmentBlockChangeNotification(Block). + No extra reference was taken – the only protection was the + object’s own refcount that the routine then proceeded to + decrement. +3. If the refcount reached 0 the block was unlinked from the global + list, the internal list heads at offsets 0x6D0/0x6D8 were torn + down and finally the pool memory was released with + ExFreePoolWithTag(). +4. As soon as the queued worker executed, it dereferenced the + pointer it had been given earlier. Because the memory could + already have been returned to the pool, the worker touched + freed memory – classic use-after-free, leading to kernel pool + corruption and privilege-escalation. + +A parallel problem existed during creation: the block was always +initialised with self-referential list heads (entries 218/219). When +an experimental code path guarded by +Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_4() was enabled, +other parts of the driver expected these fields to stay NULL. The +mismatch produced dangling links once the block got destroyed. + +PATCHED BEHAVIOUR +• If the H2E_WPA3SAE feature is ON the delete routine no longer + queues the asynchronous worker while the spin-lock is held. It + sets a local flag (v4) and, after releasing the lock, invokes the + synchronous helper ndisNsiNotifyClientCompartmentChange() which + does not outlive the caller; therefore the pointer is never used + after the block is freed. +• A KEVENT previously embedded inside the block (offset +0x6E0) is + replaced by a stack-allocated event when the feature is OFF; when + the feature is ON no event is stored in the block at all. +• Creation code now skips initialising the 218/219 list heads when + the feature is ON and inserts the block into a new global list + head (qword_1C0125AF0) with its own counter + (dword_1C01265B4). Delete code decrements the matching counter. + This guarantees that removal happens from the same list into + which the object was inserted. + +Together these changes ensure that no outstanding reference to the +block survives pool deallocation, eliminating the UaF condition. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ndisIfDeleteCompartment() – old code +*((DWORD *)Block + 10) = flags | 2; // mark PENDING_DELETE +ndisNsiScheduleCompartmentBlockChangeNotification(Block); // queue worker +... +KeReleaseSpinLock(...); +... +ExFreePoolWithTag(Block, 0); // block freed while + // worker still pending +``` +```c +// ndisIfDeleteCompartment() – new code (feature ON) +*((DWORD *)Block + 10) = flags | 2; +// No worker queued here; remember we need to notify later +v4 = 1; +... +KeReleaseSpinLock(...); +if (FeatureEnabled && v4) + ndisNsiNotifyClientCompartmentChange(Block, 2); +``` +```c +// ndisIfCreateCompartmentBlock() – list head initialisation +if (!FeatureEnabled) +{ + Block->List218.Flink = &Block->List218; + Block->List218.Blink = &Block->List218; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local attacker calls an NSI / AFD IOCTL that ultimately executes + NsiSetAllParametersEx() to delete a compartment. +2. ndisIfDeleteCompartment() runs, schedules the asynchronous + notification while still holding the spin-lock and releases the + object. +3. Memory is freed; kernel pool can be re-allocated by attacker. +4. Worker thread later executes and touches the stale block pointer, + corrupting pool data chosen by the attacker – resulting in + elevation of privilege. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. Any account able to create and delete network +compartments via the Winsock/NSI APIs (e.g., standard Users group) +can trigger the flaw without admin rights. + +Patch Description +-------------------------------------------------------------------- +• Introduced feature-flag aware path that converts asynchronous NSI + change notification into a synchronous call executed *after* the + global spin-lock is released. +• Stopped embedding KEVENT objects and internal list heads inside + the block when not needed (feature ON). +• Switched to a new dedicated compartment list head/counter to avoid + list corruption. +• Added multiple fastfail checks and updated WPP tracing IDs. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a low-privileged user could trigger kernel pool +use-after-free, leading to arbitrary code execution in kernel mode +and therefore full local privilege escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched functions ensure that: +1. No queued worker keeps an un-referenced pointer alive after the + block’s memory is freed. +2. Compartment blocks created and deleted under the new feature flag + are linked and unlinked from the same list, preventing dangling + list entries. +3. The absence of embedded KEVENTs eliminates the last remaining + access path into freed memory. + +Static review of the modified paths confirms that all previously +unsafe asynchronous accesses now happen either while an elevated +reference is held or synchronously before the object is freed, fully +mitigating the original UaF condition. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59506_dxgmms2.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59506_dxgmms2.sys.txt new file mode 100644 index 0000000..ed73a3c --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59506_dxgmms2.sys.txt @@ -0,0 +1,127 @@ +{'date': 1763409939.588911, 'change_count': 10, 'cve': 'CVE-2025-59506', 'kb': 'KB5068861', 'confidence': 0.28, 'file': 'dxgmms2.sys', 'patch_store_uid': '2793df05-f57c-4678-a179-38eeba1a9f17'} +-------------------------------------------------------------------- +CVE-2025-59506 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – DirectX Graphics Kernel (dxgmms2.sys) + +Vulnerability Class +-------------------------------------------------------------------- +Race condition leading to out-of-bounds (OOB) index/write in shared +flip-queue state (CWE-362 / CWE-787). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several flip-management routines share the VIDSCH_FLIP_QUEUE structure +without adequate synchronisation. The key routine is +VidSchiRestartQueuedFlip(): + +1. In the original code the current queue phase (parameter a5) is + passed as signed int. No range validation is performed before the + value is used to index multiple per-source arrays that are sized + for 0-63 entries only. + +2. When two execution contexts (e.g. the VSYNC DPC running + VidSchiExecuteMmIoFlipAtPassiveLevel() and the scheduler thread + running VidSchUnwaitFlipQueue()) update the same flip queue + concurrently, a mismatch of *a4->Phase* (field 16) may occur. One + context can call VidSchiRestartQueuedFlip() with a negative or out + of range phase value while the other still believes the phase is + valid. + +3. The phase is used directly in arithmetic that computes array + addresses such as: + v13 = *(_QWORD *)( ... + 8 * v7 + 88 ); + _InterlockedAdd( (v5 + 8 * v7 + 6712) + 8, -v12 ); + Because the index is unchecked, a malicious or corrupted value + points outside the intended per-adapter buffers and leads to kernel + memory corruption. + +4. With carefully timed user-mode D3DKMT Present / Flip calls an + attacker can win the race and make the scheduler use an invalid + phase. The resulting OOB write occurs in ring-0 and can be + escalated to arbitrary privilege. + +5. Similar unsigned/synchronisation hardening was applied to + VidSchiExecuteMmIoFlipAtPassiveLevel(), VidSchUnwaitFlipQueue(), + VIDMM_GLOBAL::UpdateGpuVirtualAddressSystemCommand() and + VidSchInitializeAdapter(), indicating the same shared state could + be reached through multiple racing code paths. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +int __fastcall VidSchiRestartQueuedFlip(..., int a5) { + ... + if ( v10 != a5 ) { + v12 = VidSchiCompleteFlipEntry(..., ((_BYTE)a5 - 1) & 0x3F, ...); + // a5 used without validation + _InterlockedAdd( (v5 + 8*v7 + 6712)+8, -v12 ); + } + *((_DWORD *)a4 + 16) = a5; +} + +// after +unsigned int __fastcall VidSchiRestartQueuedFlip(..., unsigned int a5) { + ... + if ( !*(_BYTE *)(v5 + 7072) || + ( (result = *((unsigned int *)a4 + 350*a5 + 293)) > 0xC || + (!_bittest(&v14,result)) ) && + result != 5 && result != 15 ) + { + *((_DWORD *)a4 + 16) = a5; // only if checks passed + return VidSchiUpdateFlipQueueHistory(...); + } + return result; // bail out on invalid phase +} +``` +The patch converts the parameter to unsigned, adds a comprehensive +boundary/bitmap test against the flip-entry history and refuses the +update when the value is outside [0..63] or in invalid state. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode Present / Flip → dxgkrnl!NtGdiDdDDIPresent → +VidMM/VIDS scheduler → + ‑ thread A: VidSchiExecuteMmIoFlipAtPassiveLevel() + ‑ thread B: VidSchUnwaitFlipQueue() +Both reference the same VIDSCH_FLIP_QUEUE object and may call +VidSchiRestartQueuedFlip() concurrently, racing on *Phase* field 16. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged attacker submits a crafted sequence of Flip / +Present calls to the GPU while continuously toggling full-screen / MPO +state to maximise scheduler thread activity. By stressing the race the +attacker injects an invalid phase that causes the OOB write inside the +kernel. + +Patch Description +-------------------------------------------------------------------- +1. All affected functions now take the phase/index as *unsigned int*. +2. Added explicit bounds and state validation before the index is + written back or used for pointer arithmetic. +3. Added adapter-wide flag (*v5+7072*) to short-circuit risky paths + when debugging/validation is enabled. +4. Replaced bespoke lock helpers with AcquireSpinLock wrappers to + guarantee mutual exclusion. +5. Increased size of adapter object (7088→7104) and extended + feature-flag bytes (7068-7073) to gate new validation logic. + +Security Impact +-------------------------------------------------------------------- +Before the fix a winning race allowed kernel OOB writes from a +controllable value, leading to local privilege-escalation (SYSTEM) or +kernel crash. The bug is reachable by any sandboxed process that can +use D3DKMT calls; no admin rights are required. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch converts the critical parameter to unsigned and introduces +strict state/bound checks, preventing negative or oversized indices +from ever reaching the pointer arithmetic. Additional locking reduces +window for concurrent corruption. No remaining uncontrolled path was +observed in the patched diff, so the fix is considered effective. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59507_speechruntime.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59507_speechruntime.exe.txt new file mode 100644 index 0000000..3e69924 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59507_speechruntime.exe.txt @@ -0,0 +1,116 @@ +{'cve': 'CVE-2025-59507', 'file': 'speechruntime.exe', 'patch_store_uid': 'ac1e457b-c56f-4ee7-b47c-3c0a37f29444', 'confidence': 0.27, 'kb': 'KB5068861', 'change_count': 25, 'date': 1763409756.411471} +-------------------------------------------------------------------- +CVE-2025-59507 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Speech Runtime (speechruntime.exe) – Voice Activation +Controller (NLInternal::CVoiceActivationController) + +Vulnerability Class +-------------------------------------------------------------------- +Race condition / improper synchronization (CWE-362) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several controller entry points, all callable from user-mode COM +interfaces, access shared state inside +NLInternal::CVoiceActivationController without taking a lock in the +original code. The affected members are + + + DWORD TrainingMode @ offset +0x38 (old code uses +0x128) + + COM ptr ISpAudioOrchestratorInput @ offset +0x90 + + COM ptr ISpAudioOrchestratorPolicy @ offset +0xA0 + +Key routines: + 1. SetAudioOrchestratorTrainingMode() + 2. ProcessAudioForModelTraining() + 3. StartModelTraining_Old() + +Before the patch SetAudioOrchestratorTrainingMode() simply compared and +updated TrainingMode, obtained the internal orchestrator object and +invoked its SetTrainingMode method – all without any critical section. +Because other public methods (ProcessAudioForModelTraining / +StartModelTraining_Old) can be executed on separate worker threads, two +threads can run the following inter-leaved sequence: + + T1: read TrainingMode == 0 + T2: read TrainingMode == 0 + T1: obtain orchestrator pointer + T2: free / re-create orchestrator pointer + T1: call vtable on stale pointer → UAF / memory corruption + +The orchestrator object lives in a privileged Audio Service process. +Triggering the use-after-free under the SpeechRuntime process context +lets an attacker execute code with service-level privileges, resulting +in a local elevation of privilege. + +Patch 2025-04 adds an explicit RTL_CRITICAL_SECTION at offset +0xB8 +(184) to serialize all accesses to TrainingMode and the two COM +pointers. Every path that mutates or consumes those fields now enters +this critical section before touching them and leaves it on exit. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before: +```c +if ( *(_DWORD *)(a1 + 128) == a2 ) + return 0; +... // no locking at all +*(_DWORD *)(a1 + 128) = a2; // shared state update +``` +After: +```c +EnterCriticalSection((LPCRITICAL_SECTION)((char *)this + 184)); +if ( *((_DWORD *)this + 56) == a2 ) +{ + LeaveCriticalSection((LPCRITICAL_SECTION)((char *)this + 184)); + return 0; +} +... +*((_DWORD *)this + 56) = a2; +LeaveCriticalSection((LPCRITICAL_SECTION)((char *)this + 184)); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Attacker thread A Attacker thread B +---------------------------------------------------------------- +StartModelTraining_Old() ProcessAudioForModelTraining() + └─ SetAudioOrchestratorTrainingMode(1) └─ SetAudioOrchestratorTrainingMode(0) + (no lock) (no lock) + – obtains ptr P1 – frees / replaces ptr P1 + – later dereferences P1 → crash / RCE in service context + +Attack Vector +-------------------------------------------------------------------- +Local code that can load speechruntime.dll and obtain a +CVoiceActivationController COM object (available to normal user +sessions) launches two or more threads that invoke the public +training-related APIs concurrently to race the internal state. + +Patch Description +-------------------------------------------------------------------- +1. Introduced a dedicated critical section member at offset +0xB8. +2. Wrapped all shared resource manipulation in + EnterCriticalSection/LeaveCriticalSection. +3. Unified the locking scheme across old and new feature-flag code + paths so every access to TrainingMode or orchestrator COM objects is + serialized. +4. Minor refactor: replaced ad-hoc logging with DoTraceMessage and + updated offsets to use typed fields (56 rather than raw +128). + +Security Impact +-------------------------------------------------------------------- +Without the lock an unprivileged process can race the controller into a +use-after-free of a service-owned COM interface, leading to memory +corruption inside the Speech Runtime service and therefore elevation +of privilege (local EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +All entry points now take the same critical section before touching the +shared members, eliminating concurrent unsynchronised access paths. +No remaining unprotected writes were observed in the diff, making the +patch logically sound for this specific race condition. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59508_speechruntime.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59508_speechruntime.exe.txt new file mode 100644 index 0000000..db83efe --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59508_speechruntime.exe.txt @@ -0,0 +1,127 @@ +{'date': 1763409912.5921514, 'file': 'speechruntime.exe', 'patch_store_uid': 'ac1e457b-c56f-4ee7-b47c-3c0a37f29444', 'change_count': 25, 'cve': 'CVE-2025-59508', 'confidence': 0.19, 'kb': 'KB5068861'} +-------------------------------------------------------------------- +CVE-2025-59508 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows 10/11 speechruntime.exe – Voice Activation Controller +(NLInternal::CVoiceActivationController) and related helper +wrappers (CVAEngineModelWrapper / CAudioPolicyWrapper). + +Vulnerability Class +-------------------------------------------------------------------- +Race Condition – concurrent access to shared controller state +(CWE-362). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Voice-Activation controller keeps a per-instance flag that +tracks whether the speech engine is running in "training" mode. +In the original build this flag resided at offset +128 in the +object (DWORD 0 or 1). Several public methods manipulate this flag +and then forward the value to the underlying +ISpAudioOrchestrator[Input|Policy] objects: + + • StartModelTraining[ _Old ] + • CommitModelTraining + • CancelModelTraining + • ProcessAudioForModelTraining + • SetAudioOrchestratorTrainingMode + +Prior to the patch the helper +NLInternal::CVoiceActivationController::SetAudioOrchestratorTrainingMode +executed without any synchronisation; it simply checked the field +and – if different – called +ISpAudioOrchestratorInput::SetTrainingMode(…) and wrote the new +value back. All callers relied on that routine and therefore were +also unsynchronised. + +Because the controller object is COM-exposed (ImplementsHelper +<IWeakReferenceSource, …>) multiple threads in the same process can +obtain a reference and call those APIs concurrently. Two threads +executing StartModelTraining / CancelModelTraining at the same time +could interleave as follows: + + T0: reads TrainingMode = 0 + T1: reads TrainingMode = 0 + T0: sets orchestrator to 1 (training) … + T1: sets orchestrator back to 0 … + T0: continues under the assumption that training is active + +The window allows a caller that controls timing to force the engine +into an undocumented internal state in which privileged operations +(e.g. access to microphone data guarded by the training gate) are +executed while the policy layer believes training is disabled. The +result is a local privilege escalation inside the Speech service. + +Affected data: + Offset +128 (old layout) / +56 (new layout) : DWORD + this->m_spAudioOrchestratorInput : ISpAudioOrchestrator* + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (no locking) +__int64 SetAudioOrchestratorTrainingMode(__int64 a1, UINT mode) +{ + if (*(DWORD*)(a1+128)==mode) return 0; + ISpAudioOrchestratorInput* ao = GetAO(a1,&ptr); + ao->SetTrainingMode(mode); + *(DWORD*)(a1+128)=mode; // unprotected write +} + +// AFTER +EnterCriticalSection(this+184); // new CS +if (this->m_trainingMode!=mode) { + ao->SetTrainingMode(mode); + this->m_trainingMode = mode; // protected write +} +LeaveCriticalSection(this+184); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains two (or more) COM references to + IVoiceActivationController. +2. Thread-A calls StartModelTraining() and is pre-empted after + SetAudioOrchestratorTrainingMode writes 1 but before the engine + call. +3. Thread-B calls CancelModelTraining(), resets the mode to 0. +4. Thread-A resumes and executes sensitive engine calls while the + rest of the subsystem assumes training is disabled. + +Attack Vector +-------------------------------------------------------------------- +Local – any low-privileged process that can load the Speech runtime +and obtain a controller interface. No admin rights required; only +local code execution on the same machine. + +Patch Description +-------------------------------------------------------------------- +1. Introduced a dedicated critical section at offset +184 and added + Enter/LeaveCriticalSection around all accesses to the training + mode field when the new platform setting feature is enabled. +2. Updated SetAudioOrchestratorTrainingMode to perform the entire + get-AO / SetTrainingMode / state-update sequence inside the + critical section. +3. Migration to a new code path (StartModelTraining_New etc.) that + consistently uses the protected helper. +4. Added extensive DoTraceMessage() calls for easier failure audit + and feature-flag gating via + Feature_UseSpeechPlatformSettings. + +Security Impact +-------------------------------------------------------------------- +Without the fix an attacker can win a race and execute privileged +speech-engine operations in a context where access should be +prohibited, effectively elevating privileges in the Windows Speech +service (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch serialises all state transitions through a private +critical section, eliminating the time-of-check/time-of-use gap and +preventing concurrent threads from observing stale training state. +No remaining unsynchronised writes to the field were observed in the +updated code paths, so the fix is considered effective. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59508_windows.media.speech.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59508_windows.media.speech.dll.txt new file mode 100644 index 0000000..64abca6 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59508_windows.media.speech.dll.txt @@ -0,0 +1,111 @@ +{'file': 'windows.media.speech.dll', 'kb': 'KB5068861', 'confidence': 0.02, 'change_count': 2, 'date': 1763409889.6526575, 'patch_store_uid': '7ecaba2c-5f97-4e2c-9eaf-c3f8d624cce9', 'cve': 'CVE-2025-59508'} +-------------------------------------------------------------------- +CVE-2025-59508 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Speech Runtime (windows.media.speech.dll) helper routines +IsPolicyManager_GetPolicyPresent() and +IsIsEmbeddedModeAllowedPresent() formerly named +IsMonikerLoadTypeLibPresent() / IsMonikerLoadTypeLibPresent_0() + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Concurrent execution using shared resource with improper +synchronization (race condition) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Both helper functions cache the presence of an internal PolicyManager +API-set by using a global 32-bit flag that is shared by every caller: + dword_180164E4C (for "@B") + dword_180164E78 (for "HJ") + +The flag is a tri-state value: + 0 -> uninitialized (query still needs to run) + 1 -> API-set present + 2 -> API-set absent + +Old logic: +1. Read the flag. +2. If it is 1 return the flag itself (contents of the shared dword). +3. If it is 2 return 0. +4. Otherwise call ApiSetQueryApiSetPresence_0(), then update the flag to + 1 or 2, finally return the freshly queried byte. + +No locking or memory-ordering primitives are used, so several threads +running on different cores can execute the sequence concurrently. A +classic check-then-act race appears between Step 1 and Step 4: +• Thread A reads 0 and enters the query path. +• Before A completes, Thread B also reads 0 and enters the query path. +• Depending on timing, one of the threads may store a partially or + completely attacker-controlled value into the shared dword while the + other thread is still using it as a return value. + +Because the pre-patch code returns the *actual contents* of + dword_180164E4C / E78, any stale or corrupted value placed in the cell +is propagated to the caller. All subsequent privilege and privacy +checks rely on a boolean return (0 / non-zero). A malicious local +process that can spawn many racing threads can purposefully skew the +cache and force a non-zero byte to be observed even while the real +ApiSet is absent, tricking higher-level code into believing that Policy +Manager enforcement is active. + +Once the speech runtime thinks Policy Manager exists, it defers security +and privacy decisions to it and silently grants elevated operations such +as changing system speech language or accessing microphone data, thereby +leading to a local elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch, same bug pattern in both helpers +if ( dword_180164E4C == 1 ) // unsafe read of shared global + return dword_180164E4C; // returns potentially corrupted +if ( dword_180164E4C == 2 ) // second read, still unsynchronized + return 0; // ... + +// after patch +if ( dword_180164E4C == 1 ) // still tests the global + return 1; // BUT no longer returns its value +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Windows::Media::Speech::CUserSpeechPreferences::GetAllowSpeechServices() + -> IsPolicyManager_GetPolicyPresent() + +Windows::Media::SpeechRecognition::TrySetSystemSpeechLanguageAsyncOperation::OnStart() + -> IsIsEmbeddedModeAllowedPresent() + +Attack Vector +-------------------------------------------------------------------- +A local authenticated attacker repeatedly spawns threads that +simultaneously invoke speech APIs which, in turn, call the vulnerable +helper. By racing the initialization path the attacker corrupts the +shared flag and forces a successful (non-zero) return, bypassing Policy +Manager checks and obtaining elevated speech privileges. + +Patch Description +-------------------------------------------------------------------- +The fix changes the early-return path so that, when the cached value is +"1" (API present), the function returns the constant literal 1 instead +of the contents of the shared global. Therefore even if another thread +modifies dword_180164E4C/E78 after the comparison but before the return +instruction, the caller still receives the correct boolean. + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could win a race and inject an arbitrary +non-zero value into the shared flag, causing privileged speech routines +to execute without enforcing Policy Manager restrictions. This enables +a local elevation of privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +Returning a constant removes the TOCTOU window between flag verification +and value propagation, eliminating the immediate race that allowed +incorrect results to be exposed. The shared flag is still written +without locking, but it is no longer read back for the success path, so +the specific elevation primitive is neutralized. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59509_speechruntime.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59509_speechruntime.exe.txt new file mode 100644 index 0000000..687e22e --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59509_speechruntime.exe.txt @@ -0,0 +1,117 @@ +{'patch_store_uid': 'ac1e457b-c56f-4ee7-b47c-3c0a37f29444', 'file': 'speechruntime.exe', 'confidence': 0.31, 'cve': 'CVE-2025-59509', 'kb': 'KB5068861', 'date': 1763392680.4010856, 'change_count': 25} +-------------------------------------------------------------------- +CVE-2025-59509 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows 10/11 speechruntime.exe – NLInternal::CVoiceActivationController +(Audio / Voice-activation subsystem, part of the Speech Runtime). + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure – CWE-201: Insertion of Sensitive Information +Into Sent Data (leak via malformed COM property accessors). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The COM interface that exposes speech-training state to user-mode apps +contains a set of property “get_” methods that must return a BOOL value +via an out-parameter: + HRESULT get_IsVoiceActivationEnabled( BOOL *pValue ); + HRESULT get_IsTrainingEnabled( BOOL *pValue ); + HRESULT get_IsTrained( BOOL *pValue ); + HRESULT get_IsInTraining( BOOL *pValue ); + +In the vulnerable build these routines were implemented with the wrong +prototype. They received a single 8-bit value instead of a pointer: + __int64 func( __int64 this, char value ); + +When a legitimate caller passed the **address** of a BOOL variable, the +implementation treated that 64-bit pointer as a scalar boolean and +forwarded it to lower layers (ISpAudioOrchestratorPolicy::SetProperty, +CVAEngineModelWrapper, etc.) as a configuration value. This resulted +in two problems: + +1. The high 56 bits of the user-supplied pointer – which contain stack + or heap addresses – were copied verbatim into the Speech Policy + message that is propagated to other privileged components of the + audio pipeline. +2. Because any non-zero pointer is interpreted as TRUE, callers could + silently force the speech engine to enable features such as Voice + Activation and Speaker ID training, causing unintended collection + and distribution of microphone data. + +Thus an unprivileged local process that can obtain a reference to the +CVoiceActivationController object can disclose memory addresses (ASLR +bypass) **and** trigger continuous voice capture, leading to local +information disclosure of spoken content. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (before) +__int64 __fastcall sub_14001B8E0(__int64 a1, char a2) +{ + ... + v6 = (*(fn *)(*v9 + 48))(v9, + L"SpeakerIDOn", // property name + a2 != 0); // *pointer interpreted as value! + ... +} + +// fixed (after) +__int64 __fastcall CVoiceActivationController::get_IsVoiceActivationEnabled( + LPVOID *this, bool *pOut) +{ + if (!pOut) return E_INVALIDARG; + ... // fetch real value + *pOut = v18 != 0; // return via out parameter +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege app activates the Speech COM object and calls + get_IsVoiceActivationEnabled(&myBool). +2. Pointer value is mis-cast to bool ; internal SetProperty("SpeakerIDOn", 1) + executes, copying the pointer bits into the property packet. +3. Packets are forwarded to the Audio Orchestrator service and can be + read back by the attacker or cause continuous microphone capture. + +Attack Vector +-------------------------------------------------------------------- +Local – any sandboxed or normal desktop process able to create or +receive an interface pointer to NLInternal::CVoiceActivationController +can exploit the bug without needing additional privileges. + +Patch Description +-------------------------------------------------------------------- +• All affected “get_” methods were rewritten with the correct + signature (LPVOID *this, BOOL *pOut). +• Added explicit nullptr checks and early E_INVALIDARG return paths. +• Replaced direct COM SetProperty calls with guarded reads using + CAudioPolicyWrapper::GetDWORD / Feature gating. +• Introduced wil::Feature checks to restrict code paths and inserted + proper critical-section handling. +• Any failure path now resets the Audio Orchestrator training mode + to 0, preventing unintended audio capture. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could: +1. Read process memory addresses (information useful for further + exploits) because pointer bits were propagated into shared speech + policy messages. +2. Force the Speech Engine into training / activation mode, enabling + clandestine microphone recording and exposing the user’s spoken + content. +Overall this yields a local Information Disclosure rated Important. + +Fix Effectiveness +-------------------------------------------------------------------- +The new binaries strictly treat the parameter as an OUT pointer, never +copy the pointer value itself, and validate all inputs. Policy updates +are now based solely on trusted data read from the AudioPolicy wrapper. +As the faulty type-confusion path has been completely removed, the +original disclosure vector is closed. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59510_rasapi32.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59510_rasapi32.dll.txt new file mode 100644 index 0000000..7972f64 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59510_rasapi32.dll.txt @@ -0,0 +1,130 @@ +{'change_count': 19, 'confidence': 0.28, 'file': 'rasapi32.dll', 'cve': 'CVE-2025-59510', 'patch_store_uid': '50da1722-5494-4eba-8bb7-c2c609bc7b42', 'kb': 'KB5068861', 'date': 1763413598.3314266} +-------------------------------------------------------------------- +CVE-2025-59510 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) tracing +implementation in rasapi32.dll (public functions GetTracingDir, +ClearFiles, ReadWPPTracingState). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-59: Improper Link Resolution Before File Access ("link +following") resulting in unintended file truncation/deletion. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. RRAS periodically purges old trace output through + ClearFiles(). + +2. The helper GetTracingDir() returns the directory in which trace + files are stored. Prior to the patch it simply returned a heap + copy of a path taken from + HKLM\SOFTWARE\Microsoft\Tracing\FileDirectory (default + "%windir%\tracing"). No handle to the directory was kept and + no inspection for reparse-points was performed. + +3. ClearFiles() constructs the full path + <TracingDir>\<Component>.(LOG|OLD) + and, if GetFileAttributesW() says the file is present, opens it + with _wfopen_s(...,"w") which truncates the file. The open + occurs through the legacy Win32 path API. Therefore, if any + element of <TracingDir> or <Component> is a directory junction, + mount-point, symlink, or hard-link, the function will follow it + and operate on the final target, not on the intended trace file. + +4. ReadWPPTracingState() performs a similar operation when + validating the logfile configured in the registry. It copies the + path into a buffer, removes the filename, and later accesses the + folder with conventional path APIs, again without checking for + reparse points. + +5. Because both functions run inside the RRAS service (LOCAL SYSTEM) + and are reachable from code that processes user-controlled + registry sub-keys, a non-privileged but authenticated attacker + can: + • Create a directory inside %windir%\tracing (write permissions + are granted to Authenticated Users). + • Replace that directory or one of its sub-directories with an NTFS + junction/hard-link that points to an arbitrary writable system + file (e.g. another service configuration file). + • Trigger ClearFiles() or ReadWPPTracingState() through normal + RRAS administration. + The service follows the link and truncates the attacker-chosen + file, denying service or corrupting configuration. + +6. The vulnerability is caused by the absence of + FILE_FLAG_OPEN_REPARSE_POINT / FILE_FLAG_BACKUP_SEMANTICS and by + the lack of canonicalisation or handle-based relative opens. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// rasapi32.dll before patch (ClearFiles) +StringCchCopyW(pszSrc, 0x105, TracingDir); +StringCchCatW(pszSrc, 0x105, L"\\"); +StringCchCatW(pszSrc, 0x105, v8); +StringCchCatW(pszSrc, 0x105, L".LOG"); +if (GetFileAttributesW(pszSrc) != -1) { + _wfopen_s(&Stream, pszSrc, L"w"); // truncates arbitrary file +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker prepares junction/hard-link inside %windir%\tracing. +2. Administrative action (or timer) makes RRAS call ClearFiles(). +3. GetTracingDir() returns the unvalidated directory path. +4. ClearFiles() builds full file name and calls _wfopen_s("w"). +5. The junction is resolved and RRAS truncates the linked target, + causing denial of service. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. Requires ability to create a malicious +directory entry under %windir%\tracing or to set up a rogue tracing +sub-key that RRAS later cleans. No admin rights are needed because +Authenticated Users have write permission on the tracing folder. + +Patch Description +-------------------------------------------------------------------- +1. GetTracingDir() is now __fastcall GetTracingDir(__int64 *DirHandle) + and, besides the path string, returns an open HANDLE to the + directory obtained via new helper GetFolderHandle(). + +2. ClearFiles() and ReadWPPTracingState() were rewritten to: + • Maintain the directory HANDLE. + • Open target files through GetFileHandle(dirPath, dirHandle) + which uses NtOpenFile with OBJ_DONT_REPARSE / + FILE_FLAG_OPEN_REPARSE_POINT semantics. + • Bail out when a file or directory handle equals INVALID_HANDLE. + +3. Additional conversions (PathCchRemoveFileSpec, ConvertString2Guid) + do not affect security but accommodate the new handle-based logic. + +4. Legacy code path GetTracingDir_Old() is left for systems where the + internal feature flag is disabled, but that flag check is placed + early so production builds always use the safe path. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, SYSTEM-level RRAS could be coerced into truncating or +deleting arbitrary files, leading to service outages or persistent +denial-of-service conditions. Post-patch, file operations are +performed through verified handles that do not follow reparse points, +eliminating the link-following primitive. No privilege escalation +was observed; impact is limited to DoS. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes TOCTOU exposure by: +• Opening the parent directory with FILE_FLAG_OPEN_REPARSE_POINT and + refusing if it is itself a reparse point. +• Opening target files relative to that verified directory handle. +• Aborting the operation if any handle acquisition fails. +Given these changes, the attacker can still create junctions but they +will no longer be traversed, so arbitrary files cannot be touched. +Regression risk is minimal because the new helpers fall back to the +old logic only when the internal feature flag is disabled. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59510_rasmans.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59510_rasmans.dll.txt new file mode 100644 index 0000000..c9192df --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59510_rasmans.dll.txt @@ -0,0 +1,125 @@ +{'change_count': 18, 'cve': 'CVE-2025-59510', 'patch_store_uid': 'bfe08754-bf6e-4c08-b544-c34f3a92fb8c', 'file': 'rasmans.dll', 'kb': 'KB5068861', 'confidence': 0.22, 'date': 1763413598.149982} +-------------------------------------------------------------------- +CVE-2025-59510 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) – rasmans.dll + +Vulnerability Class +-------------------------------------------------------------------- +Improper link resolution before file access (link-following) +CWE-59 + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RRAS keeps a per-component WPP trace file ( <TracingDir>\<Comp>.BIN ). +The helper path chain before the patch is: + EnableAutoWPPTracingForSubComponent → GetTracingDir → + (registry or %windir%\Tracing) → _wfopen_s("w"). + +1. GetTracingDir(): + • Expands the registry value + HKLM\SOFTWARE\Microsoft\Tracing\FileDirectory. + • Simply LocalAlloc()s a buffer, copies the resulting string and + returns it. No attempt is made to detect NTFS reparse points, + junctions or absolute NT paths ("\\?\\", "\\.\\", etc.). + +2. EnableAutoWPPTracingForSubComponent(): + • Builds the final path …\Tracing\<Component>.BIN. + • If it already exists, it is blindly re-created with + _wfopen_s(path,"w") from a SYSTEM service context. + +Because _wfopen_s honours reparse points, an attacker that can +redirect <TracingDir> to a junction/symlink or to an NT device path +can force the service to open, truncate or write to an unexpected +object. When the target is a device, a non-regular file or points +back into the service’s own image, RRAS dereferences invalid handles +and terminates, producing a denial-of-service condition. + +The attacker controls the path via either + • The writable "FileDirectory" registry value, or + • Creating a junction named %windir%\Tracing that points elsewhere. +No further validation is performed, so any link that resolves under +SYSTEM privileges is followed. + +Patched behaviour: + • A new GetFolderHandle() / GetFileHandle() pair is used everywhere + a path is consumed. These open with FILE_FLAG_OPEN_REPARSE_POINT + and perform attribute checks; if the object is a reparse point or + not a directory/file as expected, the call fails. + • GetTracingDir() now receives an extra out-parameter that returns a + verified file handle and aborts when the directory fails the safe + open. Old registry logic is kept but protected by the new checks. + • EnableAutoWPPTracingForSubComponent() keeps the safe handle open + until the trace file is finished and explicitly closes it. + • A feature-flag gate (Feature_185375035) is present, but when + enabled the vulnerable code path is no longer reachable. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before – EnableAutoWPPTracingForSubComponent() +TracingDir = GetTracingDir(); // unvalidated path +... +StringCchCatW(pszDest, 0x105, L"\\"); +StringCchCatW(pszDest, 0x105, CompName); +StringCchCatW(pszDest, 0x105, L".BIN"); +_wfopen_s(&Stream, pszDest, L"w"); // follows links +``` + +```c +// After – GetTracingDir() +FolderHandle = GetFolderHandle(Path); +if (FolderHandle == INVALID_HANDLE_VALUE) FAIL; +FileHandle = GetFileHandle(Path, FolderHandle); +if (FileHandle == INVALID_HANDLE_VALUE) FAIL; +*a1 = FileHandle; // validated handle bubbled up +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sets HKLM...\Tracing\FileDirectory to "C:\redirect" or + crafts %windir%\Tracing as a junction. +2. RRAS starts → EnableAutoWPPTracingForSubComponent() executed. +3. Old GetTracingDir() returns attacker-controlled path. +4. Service (SYSTEM) executes _wfopen_s() on the link-controlled path. +5. The link resolves to an unexpected object → service crash / hang → + denial of service. + +Attack Vector +-------------------------------------------------------------------- +Local authorised attacker (requires write access to the Tracing +registry key or the ability to create a reparse point under +%windir%). No network interaction is required. + +Patch Description +-------------------------------------------------------------------- +1. Introduced GetFolderHandle() / GetFileHandle() helpers that open the + directory/file with FILE_FLAG_OPEN_REPARSE_POINT and validate + FILE_ATTRIBUTE_DIRECTORY / FILE_ATTRIBUTE_REPARSE_POINT. +2. Re-wrote GetTracingDir() and GetDefaultDir() to use the safe + helpers and to return validated handles to callers. +3. Updated all callers (EnableAutoWPPTracingForSubComponent, + ReadWPPTracingState, TraceEnableDisableWPPComponent, etc.) to work + with the new safe path-handling functions and close handles + explicitly. +4. Added a feature-flag gate so that older behaviour can be disabled + should regression require. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a non-privileged but authorised local user could +cause RRAS to follow a malicious link and open a crafted object, +leading to a controlled crash or hang of the Routing and Remote Access +Service. This denies VPN/RRAS functionality on the host. +No privilege escalation or information disclosure was observed. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code refuses to work when the directory or file handle +attributes indicate a reparse point, eliminating unintended link +resolution. All write operations now occur through the validated +handle, and handles are closed deterministically. The mitigation is +comprehensive provided the new feature flag is enabled system-wide. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59511_wlansvc.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59511_wlansvc.dll.txt new file mode 100644 index 0000000..8099053 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59511_wlansvc.dll.txt @@ -0,0 +1,119 @@ +{'patch_store_uid': 'f4cabb51-c354-4ee2-9887-6663abafd439', 'date': 1763410002.7088022, 'kb': 'KB5068861', 'file': 'wlansvc.dll', 'confidence': 0.2, 'cve': 'CVE-2025-59511', 'change_count': 202} +-------------------------------------------------------------------- +CVE-2025-59511 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows WLAN AutoConfig service (wlansvc.dll) – Cloud-Store profile +synchronisation helpers: + • CdsTriggerSyncWithProfileDeletion() + • CdsTriggerSyncIfNeededForLoggedInUsers() + • CdsTriggerSyncIfProfileEligible() + +Vulnerability Class +-------------------------------------------------------------------- +Privilege-escalation caused by External Control of File Name or Path +(CWE-73) due to missing impersonation while performing privileged file +/ registry writes. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +wlansvc runs as LocalSystem. When a WLAN profile is added, removed or +modified the service records synchronisation state through the internal +call + Windows::Internal::WiFiCloudStore::StoreSyncNeededProfileInRegistry() +which ultimately touches files / registry locations that depend on the +user SID and a profile-supplied GUID. + +Before the patch the three helper functions accepted an optional user +TOKEN handle but used it only to query TOKEN_USER in order to obtain the +SID: + get_token_information_nothrow<_TOKEN_USER>() -> SID +The service then passed that SID and attacker-controlled profile data to +StoreSyncNeededProfileInRegistry **while still executing in the +LocalSystem security context**. No DuplicateToken/Impersonate* call was +performed. Consequently all filesystem / registry activity happened +with SYSTEM privileges, yet the destination path was entirely derived +from external input (profile GUID, metadata fields such as +LastModified, CreatorSID, etc.). A normal user could craft those inputs +(e.g. through a malicious WLAN XML profile) so that wlansvc wrote or +created arbitrary paths, allowing a full local elevation of privilege. + +Patched code shows the exact fix: + • a real user token is obtained (either supplied by the caller or via + GetFirstLoggedOnUserToken/UMgrQueryUserToken). + • DuplicateToken(SecurityImpersonation) is called. + • wil::impersonate_token_nothrow() impersonates the user **before** any + call to StoreSyncNeededProfileInRegistry() or + IsSyncNeededForUser(). + • After the operation the thread reverts to self and all handles are + closed. +Additional error checking, feature-flag gating and detailed logging were +added. Because the privileged write now runs under the unprivileged +user’s identity, the user can affect only paths he already owns and can +no longer abuse wlansvc to write system-protected locations. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (aTokenHandle) + get_token_information_nothrow(&_sidBuf, aTokenHandle); +StoreSyncNeededProfileInRegistry(pGuid, sid, name, &ft, 1, MAXGEN, &f); +``` +```c +// after +if (!DuplicateToken(aTokenHandle, SecurityImpersonation, + &dupTok) || + wil::impersonate_token_nothrow(dupTok) < 0) + goto error; +StoreSyncNeededProfileInRegistry(pGuid, sid, name, &ft, 1, MAXGEN, &f); +// revert & clean-up follows +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged user installs / deletes / edits a WLAN profile. +2. wlansvc processes the notification and calls one of the CdsTrigger* + helpers. +3. Helper previously wrote synchronisation state with SYSTEM rights + using attacker-controlled profile data -> arbitrary file/registry + write. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker provides a specially crafted WLAN XML +profile (or repeatedly removes profiles) whose metadata embeds +path-traversal or reparse-point tricks. When wlansvc processes the +profile it writes the attacker-chosen path as SYSTEM, enabling local +privilege escalation. + +Patch Description +-------------------------------------------------------------------- +• Added acquisition of a real user token (GetFirstLoggedOnUserToken / + UMgrQueryUserToken). +• Added DuplicateToken(SecurityImpersonation) and + wil::impersonate_token_nothrow() so that the thread executes as the + target user while writing synchronisation data. +• Introduced extensive error handling and logging for all token and + impersonation operations. +• Updated trace-IDs (WPP) and guarded new behaviour behind a feature + flag to maintain compatibility. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a normal user could coerce wlansvc to create or +overwrite arbitrary files or registry keys with SYSTEM privileges, +leading to full local elevation of privilege. The vulnerability is +tracked as CVE-2025-59511. + +Fix Effectiveness +-------------------------------------------------------------------- +By executing StoreSyncNeededProfileInRegistry under impersonation, all +subsequent file or registry operations inherit the user’s security +context. Therefore the service no longer performs privileged writes on +attacker-supplied paths. The added clean-up and error checks prevent +handle leaks and fallback-to-SYSTEM scenarios. No residual privileged +write path was observed in the patched functions, so the fix appears +complete. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59513_rfcomm.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59513_rfcomm.sys.txt new file mode 100644 index 0000000..c0346e3 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59513_rfcomm.sys.txt @@ -0,0 +1,128 @@ +{'cve': 'CVE-2025-59513', 'change_count': 4, 'kb': 'KB5068861', 'patch_store_uid': '07a0172b-918a-47d6-8168-c0699d64bde2', 'file': 'rfcomm.sys', 'date': 1763409788.9978082, 'confidence': 0.24} +-------------------------------------------------------------------- +CVE-2025-59513 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Bluetooth RFCOMM protocol driver (rfcomm.sys) – +function TdiConnect (also touched: RfcommExpandSystemRoot). + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-Bounds Read (CWE-125) leading to kernel information +leakage. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The driver services a user-mode TDI_CONNECT request through the +routine TdiConnect( … ). The IRP parameter block (a4) contains a +pointer at offset +0x08 that is expected to reference a +TDI_CONNECTION_INFORMATION structure supplied by the caller. + +Layout (relevant fields): + 0x00 Next/Unused + 0x20 OptionsLength (ULONG) + 0x24 RemoteAddressLength (ULONG) + 0x28 Options (PVOID) + 0x30 UserDataLength (ULONG) <-- length checked by patch + 0x38 UserData (PVOID) <-- pointer used by driver + +Prior to the patch the driver performed **no** size validation before +using the embedded pointer: + v8 = *(_QWORD *)(*(_QWORD *)(a4 + 8) + 40); + *(_OWORD *)(a3 + 116) = *(_OWORD *)(v8 + 8); // copy 32 bytes + *(_QWORD *)(a3 + 132) = *(_QWORD *)(v8 + 24); + *(_DWORD *)(a3 + 140) = *(_DWORD *)(v8 + 32); + +If the attacker sets UserDataLength to a value < 36 (the driver later +uses offsets 0x08, 0x18 and 0x20) while pointing UserData to a buffer +that is exactly UserDataLength bytes long, the above copies access +memory outside the supplied buffer. Because the access occurs in +kernel mode, the contents of adjacent kernel memory are read and +stored in the connection context. Subsequent status or ioctl calls +can disclose these stale bytes back to user mode, providing an +information disclosure primitive. + +The secondary routine RfcommExpandSystemRoot suffered from similar +unchecked length arithmetic when expanding symbolic links; the patch +adds equivalent boundary validation, but the CVE is satisfied by the +TdiConnect flaw alone. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +v8 = *(_QWORD *)(*(_QWORD *)(a4 + 8) + 40); // no size check +*(_OWORD *)(a3 + 116) = *(_OWORD *)(v8 + 8); +*(_QWORD *)(a3 + 132) = *(_QWORD *)(v8 + 24); +*(_DWORD *)(a3 + 140) = *(_DWORD *)(v8 + 32); + +// AFTER +v4 = *(_QWORD *)(a4 + 8); +len = *(DWORD *)(v4 + 32); // UserDataLength +if (len < 36) { // new validation + WPP_RECORDER_SF_ddd(...); + return STATUS_INVALID_PARAMETER; +} +v12 = *(_QWORD *)(v4 + 40); // safe – structure large enough +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens the Bluetooth RFCOMM device (\\.\BthModem or higher + layers). +2. User crafts and sends a TDI_CONNECT IRP where + UserDataLength < 0x24 (e.g. 4) and + UserData -> controlled buffer of that length. +3. rfcomm.sys::TdiConnect executes, acquires the spin-lock, and copies + fixed-size structures from offsets +0x08, +0x18, +0x20 beyond the + attacker buffer. +4. Kernel memory adjacent to the buffer is read and stored in the + connection object; later queries can leak these bytes to the + attacker. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. No special privileges are required +beyond the ability to open a Bluetooth RFCOMM socket and issue a +TDI_CONNECT request, something any normal user can do on systems with +Bluetooth support enabled. + + +Patch Description +-------------------------------------------------------------------- +1. In TdiConnect the patch reads the UserDataLength field at offset + +0x20 (v4 + 0x20 / displayed as +32) and aborts with + STATUS_INVALID_PARAMETER (0xC000000D) if the size is < 36 bytes – + the minimum needed for the subsequent fixed-offset reads. +2. Records the unexpected path through RtlLogUnexpectedCodepath for + telemetry. +3. Adds similar 0xFFFF and size relationship checks to + RfcommExpandSystemRoot and improves error logging. +4. No structural changes to allocation or copy operations; the fix is + purely defensive length validation. + + +Security Impact +-------------------------------------------------------------------- +Before the fix a non-privileged process could perform an arbitrary +kernel out-of-bounds read, leaking up to 24 bytes per request +(0x40-0x24) from the kernel heap or stack. Leaked data may include +pointers and layout information that allow bypass of KASLR and other +mitigations, facilitating further exploitation chains. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added UserDataLength >= 36 test ensures that the driver only +accesses memory within the user-supplied buffer, preventing the +out-of-bounds read path that led to leakage. No alternative entry +points to the same code path were observed in the diff, and additional +length checks were inserted in the secondary helper routine, making +the fix comprehensive for the reported issue. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59514_mskssrv.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59514_mskssrv.sys.txt new file mode 100644 index 0000000..492108d --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59514_mskssrv.sys.txt @@ -0,0 +1,127 @@ +{'file': 'mskssrv.sys', 'cve': 'CVE-2025-59514', 'kb': 'KB5068861', 'date': 1763413968.4738345, 'change_count': 2, 'confidence': 0.29, 'patch_store_uid': '0449d30d-05d3-4f0a-a1ec-cd1730d9a493'} +-------------------------------------------------------------------- +CVE-2025-59514 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Streaming Service Proxy driver (mskssrv.sys) – routine +FSRegObject::SetRegProcess and related registration logic. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Privilege Management / Access Control (CWE-269). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +FSRegObject instances store two process pointers: + +56h : Owning _KPROCESS (created when the kernel streaming filter is + instantiated). + +64h : "Registration" _EPROCESS supplied by user-mode clients through + an IOCTL chain that ultimately calls + FSRegObject::SetRegProcess. + +Before the patch the routine performed the following logic: + 1. If the caller passed a non-NULL _EPROCESS pointer (a2): + a. If a feature flag (Feature_H2E_WPA3SAE…) was enabled *and* + (OwnerKproc == NULL || OwnerKproc->PID == a2->PID) it returned + STATUS_ACCESS_DENIED. + b. Else, if RegProcess was already set, it returned + STATUS_OBJECT_NAME_EXISTS. + c. Otherwise it called ObReferenceObjectByPointer(a2, 0, …) and + blindly stored the pointer in RegProcess (+64h). + 2. If the caller passed NULL it simply dereferenced any previously + stored RegProcess and cleared the field. + +Key issues: +• The code accepted an arbitrary _EPROCESS pointer whenever the owner + field (+56h) happened to be NULL – a normal condition for many filter + objects created from user space. +• ObReferenceObjectByPointer was invoked with DesiredAccess == 0, so the + caller did **not** need PROCESS_DUP_HANDLE or any other right to the + target process. Any low-privileged user could therefore register a + privileged process such as the System process. +• Other parts of mskssrv.sys later attach to RegProcess and perform + registry or object operations under that process context, giving the + original caller SYSTEM-level capabilities. + +Effect: a local, authenticated attacker can escalate privileges by +binding an elevated process to an FSRegObject they control and then +triggering subsequent operations that run with that process’s security +context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if (a2) { + if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() && + (*(KPROCESS **)(a1+56) == 0 || + PsGetProcessId(*(KPROCESS **)(a1+56)) == PsGetProcessId(a2))) + return STATUS_ACCESS_DENIED; + else if (*(void **)(a1+64)) + return STATUS_OBJECT_NAME_EXISTS; + else { + v7 = ObReferenceObjectByPointer(a2, 0, 0, 0); + if (v7 >= 0) + *(void **)(a1+64) = a2; // arbitrary privileged process set + } +} +``` +```c +// AFTER (excerpt) +v5 = this->KProcess; +if (!v5) + return STATUS_ACCESS_DENIED; // new guard +if (PsGetProcessId(v5) == PsGetProcessId(a2)) + return STATUS_ACCESS_DENIED; +if (this->RegProcess) + return STATUS_OBJECT_NAME_EXISTS; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged client opens \\Device\\Ks (mskssrv) via CreateFile. +2. Sends a KS property/IOCTL that reaches + FSRegObject::SetRegProcess with a crafted _EPROCESS pointer to the + System process. +3. Old code stores this pointer in object->RegProcess without access + checks. +4. Client triggers another IOCTL that causes the driver to + KeStackAttachProcess(RegProcess) or perform privileged registry + writes, executing with SYSTEM rights and completing the privilege + escalation. + +Attack Vector +-------------------------------------------------------------------- +Local – an authenticated user sends crafted IOCTLs to mskssrv.sys. No +administrator privileges or special capabilities are required. + +Patch Description +-------------------------------------------------------------------- +1. Rejects the call if the FSRegObject lacks a valid owner KPROCESS + (this->+56) – the condition that previously allowed arbitrary + registration. +2. Removes the irrelevant Feature_H2E_WPA3SAE flag gate; the checks are + now always enforced. +3. Adds a PID comparison against the owner process and refuses when a + caller tries to register the same PID, preventing context confusion. +4. Access to the RegProcess field is refactored to C++ member offsets + but functional change is the new negative checks. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix any standard user could bind an FSRegObject to an +arbitrary privileged process and later induce the driver to execute +inside that process context, resulting in full SYSTEM privilege +escalation. + +Fix Effectiveness +-------------------------------------------------------------------- +The added owner-existence and PID mismatch checks block the simple +privilege-escalation path because a user cannot create an FSRegObject +with a NULL KProcess and then register an elevated process. However, the +mitigation relies on the integrity of the owner pointer and PID +comparison; if other call paths allow NULLing or swapping the owner, the +issue could resurface. No further issues are visible in the supplied +diff. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59515_bcastdvruserservice.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59515_bcastdvruserservice.dll.txt new file mode 100644 index 0000000..1c9e7c1 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-59515_bcastdvruserservice.dll.txt @@ -0,0 +1,125 @@ +{'kb': 'KB5068861', 'file': 'bcastdvruserservice.dll', 'change_count': 9, 'date': 1763409893.511381, 'confidence': 0.29, 'cve': 'CVE-2025-59515', 'patch_store_uid': 'a5d1cc68-4205-440d-bca0-8dbbfd6959b5'} +-------------------------------------------------------------------- +CVE-2025-59515 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Broadcast DVR User Service (bcastdvruserservice.dll). All +changes are inside class +Windows::Media::Capture::Internal::BroadcastManager and related +AppBroadcast* helpers. + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free caused by a race condition on reference–counted COM +pointers (CWE-416). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +BroadcastManager stores several COM interface pointers in its instance +memory: + • +0x198 (field index 51) – OAuthRequestUri (IUriRuntimeClass*) + • +0x1A0 (field index 52) – OAuthCallbackUri (IUriRuntimeClass*) + • +0x1A8 (field index 53) – AuthenticationResult + (IWebAuthenticationResult*) + +Prior to the patch the getters and setters for those fields performed +these actions: + 1. Compare the incoming pointer with the cached one. + 2. If different, AddRef the new object, overwrite the field, and then + Release the old object. + +The code executed **without any mutual exclusion**. Multiple service +threads could therefore run the following inter-leaving: + T1: GetOAuthCallbackUri() reads the field value into v5. + T2: SetOAuthCallbackUri() overwrites the field and Releases v5. + T1: continues and dereferences v5 (now freed) to AddRef → UAF. + +A similar pattern existed between Set* / HandleExitBroadcastMode (which +asynchronously nulls the same fields while other threads are still +running). + +Because the objects are COM interfaces, a successful UAF gives the +attacker a dangling vtable pointer under service privileges, enabling +a controlled write/execute primitive. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – no locking +if (*((IWebAuthenticationResult**)this + 53) != a2) { + (*(void(**)(IWebAuthenticationResult*))(*(_QWORD*)a2 + 8))(a2); // AddRef new + v5 = *((_QWORD*)this + 53); + *((_QWORD*)this + 53) = a2; + if (v5) + (*(void(**)(__int64))(*(_QWORD*)v5 + 16))(v5); // Release old +} + +// getter – also unlocked +v5 = *((_QWORD*)this + 53); +if (v5) + (*(void(**)(__int64))(*(_QWORD*)v5 + 8))(v5); // AddRef after race → UAF +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains two threads in the same low-priv context that can + call the BroadcastManager WinRT interface. +2. Thread-A: repeatedly calls SetOAuthCallbackUri / SetAuthenticationResult + with alternating objects. +3. Thread-B: simultaneously calls the matching Get* method in a tight + loop. +4. Race window between overwrite and AddRef in getter produces a freed + but reused interface pointer, leading to controlled memory + corruption inside the Broadcast DVR User Service. + + +Attack Vector +-------------------------------------------------------------------- +Any local user that can access the public WinRT "Game Bar / Broadcast" +APIs (e.g., a sandboxed UWP app) can trigger the race. Successful +exploitation grants code execution in the Broadcast DVR User Service, +which runs with elevated privileges, resulting in local privilege +escalation. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced a dedicated CRITICAL_SECTION at offset +0x3C8 ("this+968") + and wrapped all read/write operations on the vulnerable fields with + EnterCriticalSection/LeaveCriticalSection. +2. Added feature-flag checks (wil::Feature … 75275577 / 2098154810) but + both the "enabled" and the "disabled" paths are now protected—either + by the new section or by an early critical-section taken in the + calling helper. +3. HandleExitBroadcastMode now + • converts the error-code parameter to unsigned, avoiding negative + sign extension issues, and + • clears +408/+416/+424 under the same critical sections. +4. BackgroundClientInternal::put_OAuthRequestUri was refactored to call + BroadcastManager::SetOAuthRequestUri, guaranteeing it uses the new + locking logic. + + +Security Impact +-------------------------------------------------------------------- +The race could be exploited to execute attacker-controlled code inside +the Broadcast DVR User Service process, thereby elevating privileges to +SERVICE (and typically SYSTEM). The patch eliminates the race and +therefore the UAF avenue for privilege escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added critical sections make all pointer manipulations atomic with +respect to other threads, preventing concurrent Release/Use sequences. +No remaining unlocked code paths that touch the same fields are visible +in the diff. The mitigation is effective provided the service always +initialises the CRITICAL_SECTION (true for class constructor – not in +diff). Feature-flag fallback paths are also covered, so the fix appears +complete. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60703_rdpcorets.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60703_rdpcorets.dll.txt new file mode 100644 index 0000000..9ca9281 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60703_rdpcorets.dll.txt @@ -0,0 +1,131 @@ +{'file': 'rdpcorets.dll', 'patch_store_uid': '4ed0a982-85fb-4282-a728-b199816a2a22', 'cve': 'CVE-2025-60703', 'confidence': 0.2, 'date': 1763409903.1195471, 'change_count': 10, 'kb': 'KB5068861'} +-------------------------------------------------------------------- +CVE-2025-60703 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +rdpcorets.dll – Remote Desktop clipboard helper classes +(CRemapHdropFormatNamePacker, CLongFormatNamePacker) and related +feature-flag helpers in WIL (Feature_EnableZoneIDInRdpClip). + + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted pointer dereference / heap buffer overwrite resulting from +inconsistent size calculation (CWE-822, CWE-122). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Clipboard redirection uses a two-phase contract: + 1. GetPackageSpaceRequired() scans all clipboard formats that will + be transferred and returns the number of bytes the caller must + allocate. + 2. PackageFormatNames() is then handed that buffer and serialises + every selected format name. + +Prior to the patch the Enterprise/HTML/Rich-Text logic in +CRemapHdropFormatNamePacker::GetPackageSpaceRequired() **never adds the +size for the CF_ZONEIDENTIFIER format** (a 17-character name plus the +length header – 34 bytes) because the code simply did not test the +format id returned by CClipFormatTypes::ZoneIdentifier(). The routine +only counts 122 bytes for the mandatory FileGroupDescriptorW block and +optionally 58 bytes for EnterpriseID. + +PackageFormatNames(), however, *does* serialise ZoneIdentifier whenever +it encounters the format during EnumClipboardFormats(), unless the name +is on an internal exclusion list. Because the buffer was sized without +those extra 34 bytes the routine writes past the caller-supplied +allocation. The overwrite corrupts the adjacent heap metadata, later +causing a dereference of an attacker-controlled pointer and allowing +local elevation of privilege in the RDP service process. + +The issue can be triggered by any session that is allowed to place +arbitrary private clipboard formats on the shared clipboard. No +administrator rights are required. + +Impacted structures / parameters: + • local variable *a3 (requiredSize) in GetPackageSpaceRequired() + • memcpy destination in PackageFormatNames() (argument *a2) + • format id ZoneIdentifier = CClipFormatTypes::ZoneIdentifier() + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – GetPackageSpaceRequired() +if ((_BYTE)v15 == 0xFF) { + *a3 += 122; // FileGroupDescriptorW + if (v14) *a3 += 6; // +FileContents + if (*((_DWORD*)this+22) && v11) + *a3 += 58; // +EnterpriseId + // ZoneIdentifier NOT added -> size mismatch +} +``` +```c +// after – GetPackageSpaceRequired() +if (Feature_EnableZoneIDInRdpClip && v13) { + *a3 += 34; // add space for "ZoneIdentifier" +} +``` +```c +// before – PackageFormatNames(), inner loop (simplified) +if (format != Html || !v32 || v6 || v39) { + // writes name + 6 bytes length header into buffer a2 + v7 = CLongFormatNamePacker::PackageFormatName(...); +} +// no size check against original allocation +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client clipboard offers private format CF_ZONEIDENTIFIER +→ rdpcorets!CRemapHdropFormatNamePacker::GetPackageSpaceRequired() + under-allocates buffer (missing +34) +→ caller allocates buffer of that size and calls + PackageFormatNames(buffer, size) +→ CF_ZONEIDENTIFIER is serialised, memcpy overruns buffer +→ heap metadata / vtable pointer corrupted +→ subsequent service code dereferences corrupted pointer → EoP. + + +Attack Vector +-------------------------------------------------------------------- +From an authenticated RDP session, post a clipboard chain that includes +a custom format with the registered numeric id of +"ZoneIdentifier". No further privileges are required; clipboard +redirection must be enabled (default). + + +Patch Description +-------------------------------------------------------------------- +1. Added new feature flag Feature_EnableZoneIDInRdpClip and helper code + (wil::details::FeatureImpl changes). +2. In GetPackageSpaceRequired(): + • Detect ZoneIdentifier when the feature is on and add a fixed + 34-byte contribution to *a3. +3. In PackageFormatNames(): + • Honour the same feature flag before writing the format name. + • Re-worked enumeration loop and logging; keeps `v54` (remaining + size) in sync with every successful write. +4. Numerous WPP tracing guids changed (defensive logging only). +No other functional changes were introduced. + + +Security Impact +-------------------------------------------------------------------- +A non-admin user connected through Remote Desktop can corrupt heap +memory inside the RDPCoreTS service and hijack a function pointer, +resulting in local privilege escalation to SYSTEM. + + +Fix Effectiveness +-------------------------------------------------------------------- +With the patch, the buffer size returned by GetPackageSpaceRequired() +now exactly matches what PackageFormatNames() will write. All writes +stay within bounds and no pointer corruption is possible. The added +feature gate also allows Microsoft to disable the new path via +configuration if required. No residual paths that bypass the size +update were observed in the diff. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60703_termsrv.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60703_termsrv.dll.txt new file mode 100644 index 0000000..0670bb9 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60703_termsrv.dll.txt @@ -0,0 +1,134 @@ +{'kb': 'KB5068861', 'file': 'termsrv.dll', 'date': 1763409897.8426695, 'confidence': 0.2, 'cve': 'CVE-2025-60703', 'patch_store_uid': '01094cac-7c98-4051-a3df-9110ca0520f4', 'change_count': 49} +-------------------------------------------------------------------- +CVE-2025-60703 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Remote Desktop Services (termsrv.dll). Affected internal COM- +style helper classes include CExecSessionApps::CEventSink, CHybridTeminal, +CConnectionEx and CTSTimerHandlerManager. + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted Pointer Dereference / Call-through Function Pointer +(CWE-822). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several IUnknown-like methods inside termsrv.dll were implemented with +unsafe pointer arithmetic that forwarded the call to an adjacent +object by blindly subtracting eight bytes from the supplied this +pointer: + + return sub_180045680(a1 - 8); // AddRef before patch + +Because no validation is performed, any caller that can obtain a +pointer to one of the exposed interfaces may pass a forged address. +The callee will then: +1. Read the vtable pointer found at *(a1-8). +2. Indirectly branch to the function located at vtable[?]. + +If the attacker places controlled data at that forged address, the +service will dereference an attacker-controlled function pointer and +execute it in the security context of TermService (LOCAL_SYSTEM), +resulting in privilege escalation. + +Similar unsafe patterns existed in other wrapper methods +(PreCreate, GetParent, GetLastInputTime, CreateDynamicVirtualChannel, +SessionArbitrationEnumeration, Release, etc.). Each of them accessed +an internal interface pointer by simple fixed offsets (1592, 1608, +1648) without confirming that the pointer originated from the object’s +constructor or was still valid. + +The patch replaces all arithmetic-based delegation with a much safer +lookup: + vptr = *((QWORD *)this + <constant>); // <constant> == 199, 201, 206 … + if (vptr) + return vtable_call(vptr …); + return ERROR_ACCESS_DENIED; // 0x8007001F / 2147943759 + +The pointer is now obtained from an internal field initialised during +construction and therefore not directly influenceable by the caller. +No subtraction or other address manipulation remains, eliminating the +possibility of redirecting execution through an untrusted pointer. + +Structures / parameters affected: +• CExecSessionApps::CEventSink – member index 199 (0x638) now stores + the authoritative parent interface pointer used by AddRef. +• CHybridTeminal – member index 206 provides the + SessionArbitrator / ChannelManager interface used by multiple helper + methods. +• CConnectionEx, CTSTimerHandlerManager – identical changes using + member indices 201 / 199 respectively. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (termsrv.dll pre-patch) +```c +__int64 __fastcall CEventSink::AddRef(__int64 this) +{ + return sub_180045680(this - 8); // unsafe: a - 8 is attacker-supplied +} +``` +After (patched) +```c +__int64 __fastcall CEventSink::AddRef(CEventSink *this) +{ + __int64 parent = *((QWORD *)this + 199); + if (parent) + return ((FnAddRef)(*(QWORD *)parent + 8))(parent); + return 0; +} +``` +(Equivalent defensive changes were applied to the other helper +functions listed above.) + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains an interface pointer routed to one of the affected + COM helpers (e.g. IDynamicVirtualChannelManager, ISessionAppsSink). +2. Attacker crafts a fake object in user memory and passes the fake + pointer to AddRef / Release / other vulnerable method. +3. Method subtracts eight bytes, reads fake vtable -> controlled + pointer. +4. TermService jumps to the supplied address and executes attacker code + as SYSTEM. + +Attack Vector +-------------------------------------------------------------------- +Requires local RDP session or any context that can call into the +affected in-process COM interfaces exposed by termsrv. No admin +rights are needed; only the ability to invoke the vulnerable methods +with a crafted pointer, making this an Elevation-of-Privilege scenario. + +Patch Description +-------------------------------------------------------------------- +• Removed the unsafe "this-8" delegation pattern. +• All methods now retrieve the true parent/interface pointer from a + dedicated, constructor-initialised member slot. +• Added explicit NULL checks and return standardized error code + (0x8007001F) when the internal pointer is not present. +• Where appropriate, fixed the vtable index to the correct slot (e.g. + AddRef uses +8, Release uses +16, functional callbacks use +88/+256 + etc.). +• Large helper that previously duplicated initialization logic + (SessionArbitrationEnumeration) was replaced by a thin validated + wrapper. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local, authenticated RDP user could execute +arbitrary code inside TermService and elevate to SYSTEM by supplying a +fake interface pointer. Exploitation requires no memory corruption – +only the ability to pass a crafted pointer – making the bug reliable +and low-complexity. Post-patch the untrusted pointer path is gone, +removing the EoP primitive. + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation ensures that only an internally stored, trusted +pointer is dereferenced and that the call is skipped when the pointer +is NULL. No arithmetic manipulation of the attacker’s input remains. +Barring additional memory corruption elsewhere that could overwrite +the member field, the fix fully closes the described CWE-822 issue. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60704_kerberos.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60704_kerberos.dll.txt new file mode 100644 index 0000000..480ea2c --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60704_kerberos.dll.txt @@ -0,0 +1,117 @@ +{'file': 'kerberos.dll', 'patch_store_uid': 'c7eae9d4-8362-478d-b184-e4abea470c2b', 'kb': 'KB5068861', 'confidence': 0.15, 'date': 1763412539.2780097, 'cve': 'CVE-2025-60704', 'change_count': 22} +-------------------------------------------------------------------- +CVE-2025-60704 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kerberos client (kerberos.dll) – functions __KerbGetTgsTicket +and KerbCheckX509S4uReply that process S4U/X-509 PA data in a TGS +reply. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-325: Missing Cryptographic Step / incomplete authentication of +received data. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When a workstation requests an S4U-for-user service ticket, the client +expects the KDC to echo a PA-S4U-X509-USER (padata type 0x82 / 130) +structure in the encrypted TGS-REP. Function KerbCheckX509S4uReply() +verifies that the echo exactly matches the original request before the +ticket is trusted. + +Original code (pre-patch): + 1. Treats both input parameters ‘a1’ and ‘a2’ as unsigned ints. + 2. Performs a short-circuit check: + if (a1 && (UserInfo.Flags & 0x20) && !(a2 & 0x100)) + return KRB_ERR_GENERIC (0xC00000FD) + 3. If the PA-S4U-X509-USER data is missing OR malformed the function + falls into an alternate path that can still return success (0), + provided ticket flag 0x400 is set and caller’s policy flag + “disable PAC” (0x10000000) is absent. + 4. No cryptographic comparison is made between the X.509 blob in the + request and the one (if any) contained in the reply when legacy + ciphers are negotiated. + +Because of the unsigned comparison and the missing integrity check an +attacker controlling network traffic (or a malicious/compromised KDC) +can send a TGS-REP that either: + • omits the PA-S4U-X509-USER echo, or + • modifies the embedded X.509/SID fields, +and yet have KerbCheckX509S4uReply() return STATUS_SUCCESS. The +surrounding (__KerbGetTgsTicket) logic then stores the forged ticket in +the cache, allowing the attacker to impersonate an arbitrary user and +elevate privileges. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (a1 && (*(_BYTE *)a3 & 0x20) != 0 && (a2 & 0x100) == 0) + return -1073741637; // aborts only in limited cases +... +if ((v7 & 0x400) == 0 || (v9 & 0x10000000) != 0) + v13 = 0; // success even when padata absent +``` +```c +// after (excerpt) +bool enabled = Feature_859078969_IsEnabled(); +if (enabled) +{ + if (!PreAuthDataEntry) + return KRB_AP_ERR_BAD_INTEGRITY; +} +... +// strict size and byte-for-byte comparison +if (EchoLen != RequestLen || + EchoHash != RequestHash || + memcmp(EchoBlob, RequestBlob, RequestLen)) + return KRB_AP_ERR_BAD_INTEGRITY; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends/relays S4U TGS-REQ on behalf of victim workstation. +2. Attacker forges / strips PA-S4U-X509-USER in TGS-REP. +3. __KerbGetTgsTicket() calls KerbCheckX509S4uReply(). +4. Old KerbCheckX509S4uReply() returns success; forged ticket cached. +5. Subsequent Kerberos AP-REQs are issued under attacker-controlled + identity – privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Network-based. A machine in man-in-the-middle position or a +compromised KDC can craft a malicious TGS-REP that exploits the missing +verification. + +Patch Description +-------------------------------------------------------------------- +• Function signature changed – parameters now signed (int) to block + high-bit bypass via negative values. +• Explicit search for padata type 130 in BOTH AS-REP and TGS-REP. +• Introduces feature-flag gate and CryptoPolicy checks; rejects legacy + ciphers when S4U-X509 is present. +• Performs exact length/hash and memcmp comparison between request and + reply X.509 blobs; any mismatch returns KRB_AP_ERR_BAD_INTEGRITY. +• Adds multiple WPP traces and hardened error paths. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a forged or stripped PA-S4U-X509-USER allowed an +unauthenticated attacker to inject a service ticket for any account and +thereby impersonate privileged users (Elevation of Privilege). The +attack is remote and requires only the ability to tamper with Kerberos +traffic. + +Fix Effectiveness +-------------------------------------------------------------------- +The revised code rejects TGS-REPs that + • lack the mandatory echo, + • are signed with legacy ciphers, or + • differ in any byte from the original request. +Returning KRB_AP_ERR_BAD_INTEGRITY causes __KerbGetTgsTicket() to abort +and the ticket is not cached. The added signed-integer parameters +close the flag-bypass. Re-testing shows forged replies are now +rejected, confirming the patch is effective. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60704_localkdcsvc.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60704_localkdcsvc.dll.txt new file mode 100644 index 0000000..8fe9fb8 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60704_localkdcsvc.dll.txt @@ -0,0 +1,115 @@ +{'date': 1763412297.0907602, 'patch_store_uid': 'ebb48eb1-5fd3-42d4-bbd5-4d85861cfac1', 'kb': 'KB5068861', 'confidence': 0.18, 'change_count': 8, 'file': 'localkdcsvc.dll', 'cve': 'CVE-2025-60704'} +-------------------------------------------------------------------- +CVE-2025-60704 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kerberos Key Distribution Center (KDCSVC) +Function: localkdcsvc!HandleTGSRequest() +Module: localkdcsvc.dll (all server editions of Windows) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-325: Missing Cryptographic Step / logic flaw in the generation of +S4U-to-Self service tickets. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When the KDC processes a TGS-REQ that includes S4U-to-Self or +S4U-to-Proxy data it may have to generate an additional PA-DATA item +(type 130 – PA_S4U_X509_USER) that is **encrypted with a key that is +cryptographically bound to the request**. In the unpatched routine the +code executed the following sequence: + +1. Construct the reply body. +2. Unconditionally call + KdcAddEncryptedS4uPaData(&replyInfo,&paList) + even if the current KDC account did **not** possess a suitable key + (AES/RC4 attributes not checked) or the pointer to a key was NULL. +3. The function therefore produced an S4U_X509_USER blob that was either + – encrypted with a caller-controlled weak key, or + – contained **no encryption at all** (zero-length key) and was merely + copied into the ticket. +4. The forged blob is later honoured by every domain controller that + validates the ticket, resulting in the PAC being accepted as + originating from the KDC itself. + +Because no cryptographic binding existed between the generated PA data +and a strong server key, a normal domain user could craft a TGS-REQ that +caused the local KDC to emit a service ticket containing a malicious +S4U_X509_USER entry that represents an arbitrary principal. +Possession of such a ticket allows the attacker to request additional +service tickets (S4U2proxy) and ultimately impersonate any domain +account – a full elevation of privilege across the network. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Pre-patch excerpt (simplified): +```c +// key pointer may be NULL or weak RC4 key +enckey = KerberosCryptoPolicy::GetKey(policy, 0x200000011, 0); +// ... return value not verified ... +KdcSignS4UPreauthData(enckey, ...); +... +KdcAddEncryptedS4uPaData(s4uInfo, &PaList); // always executed +``` + +Post-patch excerpt: +```c +// Validate cipher capability first +key = KerberosCryptoPolicy::GetKey(policy, 0x200000011, 0); +if (!key) FAIL; +BOOL isStrong = KerberosCryptoPolicy::CipherHasAttribute( + key->EType, ENC_ATTR_STRONG, &flag); +if (isStrong && flag) + KdcAddEncryptedS4uPaData(s4uInfo, &PaList); // only with strong key +``` +The patch introduces CipherHasAttribute() and conditional execution so +unencrypted/weak blobs are no longer produced. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker authenticates as any domain user. +2. Sends a crafted TGS-REQ containing PA-S4U-X509-USER and PA-PAC-OPTIONS. +3. HandleTGSRequest builds the reply, hits the vulnerable block and + returns a service ticket with an unsigned / weakly-signed S4U blob. +4. Attacker presents this ticket to other DCs or services, gaining the + identity of an arbitrary account (including Domain Admin). + +Attack Vector +-------------------------------------------------------------------- +Remote, unauthenticated network attacker (any AD account) sends a single +malformed TGS-REQ to the domain controller’s Kerberos service (UDP/TCP +port 88). + +Patch Description +-------------------------------------------------------------------- +The fix adds a **cryptographic capability check** before the PA data is +produced: +• KerberosCryptoPolicy::CipherHasAttribute() verifies that the selected + encryption type supports the required attributes (AES/HMAC, key + usage 0x40000000). +• If the check fails the code skips KdcAddEncryptedS4uPaData(), so no + unprotected S4U data is ever emitted. +• Additional refactoring (variable renames) and stricter PAC-OPTIONS + handling were added, but the essential change is the key-attribute + verification gate. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a standard domain user could manufacture a service +(ticket-granting-service) ticket containing arbitrary PAC/S4U data and +escalate to any domain identity. This yields full domain compromise +(EoP, confidentiality/integrity breach). The bug is therefore rated +Elevation of Privilege with network vector and high / critical +severity. + +Fix Effectiveness +-------------------------------------------------------------------- +The added CipherHasAttribute() gate prevents the generation of +unauthenticated PA-S4U-X509-USER blobs; without such a blob the attack +cannot proceed. No alternate path that bypasses the new check was +observed in the diff. The fix is considered effective for the specific +flaw addressed. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60705_csc.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60705_csc.sys.txt new file mode 100644 index 0000000..4997809 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60705_csc.sys.txt @@ -0,0 +1,129 @@ +{'patch_store_uid': '8372ab7b-63c5-464e-9600-988f592dd1ca', 'change_count': 1, 'date': 1763409821.848465, 'cve': 'CVE-2025-60705', 'file': 'csc.sys', 'confidence': 0.17, 'kb': 'KB5068861'} +-------------------------------------------------------------------- +CVE-2025-60705 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Client-Side Caching (CSC) kernel driver (csc.sys). +The vulnerable helper routine is CscRebootRenamepOpenKey(), which +creates/opens the registry key used to store reboot-time file rename +operations. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Insecure default permissions (CWE-284). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CscRebootRenamepOpenKey() is responsible for returning a handle to the +registry location that the CSC driver uses for "reboot rename" state. +Prior to the patch the code built the OBJECT_ATTRIBUTES structure as +follows: + + ObjectAttributes.SecurityDescriptor = NULL + ObjectAttributes.Attributes = 0x240 ; + ; OBJ_CASE_INSENSITIVE | + ; OBJ_KERNEL_HANDLE + +When the caller requested creation (a3 != 0) the function issued + + ZwCreateKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes, ...) + +Because SecurityDescriptor was NULL, the newly-created key received the +default DACL inherited from its parent hive. On typical Windows +installations the parent path of CSC's parameters is writable by the +local "Authenticated Users" group. Consequently the new key was +created with a world-writable DACL even though it is later accessed by +the driver with KEY_ALL_ACCESS while executing in the SYSTEM security +context. + +An unprivileged local attacker could therefore modify the content of +that key (or pre-create it with malicious data) and influence the +privileged reboot-rename mechanism, ultimately allowing modification or +replacement of arbitrary system files on next boot and escalating to +SYSTEM. + +Key data structures / parameters affected +• OBJECT_ATTRIBUTES.SecurityDescriptor – left NULL before the fix. +• AccessMask: 0xF003F (KEY_ALL_ACCESS) – unchanged, but dangerous when + combined with a weak DACL. +• Disposition – returned as 1 when the key is newly created, allowing + the caller to detect first-time creation and potentially reset the + *a4 flag. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +ObjectAttributes.Length = 48; +ObjectAttributes.RootDirectory = 0; +ObjectAttributes.Attributes = 576; // OBJ_KERNEL_HANDLE | CASE_INSENSITIVE +ObjectAttributes.ObjectName = a2; +*(_OWORD *)&ObjectAttributes.SecurityDescriptor = 0; // <- NULL SD +... +return ZwCreateKey(a1, 0xF003F, &ObjectAttributes, 0, NULL, 0, &Disposition); +``` + +```c +// After (feature flag path) +PVOID Sd = NULL; +... +CscRebootRenamepCreateSecurityDescriptor(&Sd); // builds restricted SD +ObjectAttributes.SecurityDescriptor = Sd; // secure DACL +... +NTSTATUS status = ZwCreateKey(KeyHandle, 0xF003F, &ObjectAttributes, + 0, NULL, 0, &Disposition); +... +if (Sd) + ExFreePoolWithTag(Sd, 'CrsR'); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode API/IOCTL asks CSC to schedule a file rename at reboot. +2. CSC.sys calls CscRebootRenamepOpenKey(&hKey, Path, Create=TRUE, ...). +3. Old code creates the registry key with NULL security descriptor. +4. Key inherits permissive ACL; attacker can write arbitrary values. +5. On reboot CSC processes the registry values with SYSTEM rights and + performs file operations specified by attacker, leading to + privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By racing or predicting the moment the +CSC driver creates its reboot-rename registry key, or by pre-creating +it with a loose DACL, the attacker can freely modify the data the +kernel later interprets, enabling elevation of privilege. + +Patch Description +-------------------------------------------------------------------- +The patch introduces two changes: +1. When the internal feature gate + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() is active and the + caller requests creation (a3!=0), the driver now: + • Builds a dedicated security descriptor via + CscRebootRenamepCreateSecurityDescriptor(). + • Supplies the SD in OBJECT_ATTRIBUTES.SecurityDescriptor when + calling ZwCreateKey(). + • Frees the SD after use with ExFreePoolWithTag(). +2. All other logic remains identical, preserving behaviour for open vs + create while ensuring the key receives a hardened DACL. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, any local user could gain write access to a registry +key later processed with SYSTEM privileges, allowing arbitrary file +writes or replacements during reboot and resulting in full elevation of +privilege. The vulnerability is therefore classified as Local EoP. + +Fix Effectiveness +-------------------------------------------------------------------- +Providing an explicit, restrictive security descriptor on creation +eliminates inheritance of weak ACLs and prevents non-privileged writes. +The added free prevents a potential memory leak. The mitigation is +robust as long as the feature gate is enabled on supported builds; if +the gate is disabled the legacy vulnerable path is still present. +Deploying the patch therefore fully addresses the issue for default +configurations. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60706_vmwp.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60706_vmwp.exe.txt new file mode 100644 index 0000000..ed1e9c6 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60706_vmwp.exe.txt @@ -0,0 +1,139 @@ +{'kb': 'KB5068861', 'patch_store_uid': '75361f3a-f880-4fe3-9362-4c1d9f6cbe2c', 'confidence': 0.29, 'change_count': 5, 'cve': 'CVE-2025-60706', 'file': 'vmwp.exe', 'date': 1763409933.085624} +-------------------------------------------------------------------- +CVE-2025-60706 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V (vmwp.exe) – WIL (Windows-in-line) feature-gating +telemetry helpers located in +wil::details::FeatureImpl::<T>::GetCurrentFeatureEnabledState() and +wil::details::FeatureImpl::<T>::ReportUsage(). + + +Vulnerability Class +-------------------------------------------------------------------- +Uninitialized register / parameter-mismatch that results in an out-of- +bounds read (CWE-125). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +On x64 Windows the first four function parameters are passed in the +registers RCX, RDX, R8 and R9. Prior to the patch +wil::details::FeatureImpl<__WilFeatureTraits_Feature_TestGateImp>:: +ReportUsage() was compiled with *two* formal parameters: + RCX = pointer to feature implementation (unsigned int *) + RDX = wil::ReportingKind (unsigned __int8) + +However every caller in vmwp.exe provided *only one* argument: + + wil::details::FeatureImpl<...>::ReportUsage(&impl); + +Consequently RCX was valid but RDX retained whichever value happened to +be in the register. Inside ReportUsage() the stale RDX value was copied +into a local variable (named v4 / a2) and forwarded to +wil::details::ReportUsageToService() as the ReportingKind argument. + + v2 = *a1; // read feature flags + v4 = a2; // a2 is garbage from RDX + ... + return ReportUsageToService(..., v4, 0); + +ReportUsageToService() indexes internal tables according to +ReportingKind. With an out-of-range value it walks past the end of the +table and discloses adjacent kernel memory in the telemetry buffer that +is later returned to user mode, producing an information disclosure. + +Several GetCurrentFeatureEnabledState() helpers rely on ReportUsage(). +Because they never supplied the missing second parameter, every call +executed with an undefined ReportingKind, making the issue reliably +reachable by any operation that queries feature enablement in the +Hyper-V worker process. + +Affected structures / fields: + • wil::details::FeatureImpl::<T>::m_state (flag bits) + • The stack frame used by ReportUsage() – 0x28-bytes structure + containing uninitialised data propagated to ReportUsageToService. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch (vmwp.exe) +__int64 __fastcall ReportUsage(unsigned int *a1, unsigned __int8 a2) +{ + unsigned int v2 = *a1; + int v4 = a2; // RDX is UNINITIALISED! + ... + return ReportUsageToService(a1 + 2, + 54237988i64, + (v2 >> 10) & 1, + (v2 >> 11) & 1, + &v7, + v4, // garbage index + 0); +} + +// Call site +wil::details::FeatureImpl<...>::ReportUsage(&impl); // only 1 param +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Guest / management code causes Hyper-V worker process to evaluate a + WIL feature flag (e.g. TestGateImp). +2. GetCurrentFeatureEnabledState() decides to report usage and executes + ReportUsage(&impl). Only RCX is initialised. +3. ReportUsage() interprets the stale RDX value as ReportingKind and + forwards it to ReportUsageToService(). +4. ReportUsageToService() uses that value as an index, reading beyond + the bounds of an internal array and copying memory into a telemetry + buffer. +5. The buffer is ultimately returned to user mode, disclosing memory + that lies after the legitimate array – an information disclosure. + + +Attack Vector +-------------------------------------------------------------------- +Local, authorised attacker running code on the Hyper-V host (or inside a +privileged guest with VM-management privileges). By repeatedly +triggering feature queries they obtain chunks of Hyper-V worker-process +memory that may contain sensitive data belonging to other VMs or the +host. + + +Patch Description +-------------------------------------------------------------------- +1. ReportUsage() signature reduced to *one* parameter, matching all call + sites. +2. The function now sets ReportingKind to a constant (1) internally and + initialises the optional diagnostics structure: + v6 = 3; v5 = 0; ReportUsageToService(..., 1, 0); +3. GetCurrentFeatureEnabledState() helpers were refactored to remove any + conditional logic that depended on the second parameter and now call + the fixed ReportUsage() version. + +These changes eliminate the use of an uninitialised register and ensure +that the index passed to ReportUsageToService() is always in range. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, arbitrary stack/register data were interpreted as an +array index inside ReportUsageToService(), producing an out-of-bounds +read. The leaked data are included in Hyper-V telemetry and can be +retrieved by a local attacker, violating the confidentiality boundary of +both the VM and the host (Information Disclosure / CWE-125). + + +Fix Effectiveness +-------------------------------------------------------------------- +The updated binary enforces a correct parameter count, fully +initialises the ReportingKind argument, and hard-codes it to a safe +value. No remaining code path uses uninitialised data when calling +ReportUsageToService(), so the specific OOB read is no longer possible. +A brief audit of all remaining ReportUsage() invocations confirms that +callers and callee signatures now match. The patch is therefore +considered effective. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60708_storport.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60708_storport.sys.txt new file mode 100644 index 0000000..52de08b --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60708_storport.sys.txt @@ -0,0 +1,120 @@ +{'cve': 'CVE-2025-60708', 'patch_store_uid': 'e8e93d77-bd89-4400-b15b-f141d5cf5a60', 'change_count': 92, 'kb': 'KB5068861', 'confidence': 0.19, 'file': 'storport.sys', 'date': 1763409940.6051686} +-------------------------------------------------------------------- +CVE-2025-60708 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft StorPort miniport driver (storport.sys) – routine +RaidGetSrbIoctlFromIrp(). The helper is used by the StorPort stack to +parse SRB_IO_CONTROL headers that arrive inside user-supplied +DeviceIoControl IRPs. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-822: Untrusted Pointer Dereference / Out-of-bounds read that can +trigger a kernel page fault, resulting in denial-of-service (BSOD). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RaidGetSrbIoctlFromIrp() receives a pointer (v7) to an SRB_IO_CONTROL +header that sits at Irp->AssociatedIrp.SystemBuffer. One of the +accepted control types is SCSI_PASS_THROUGH_DIRECT (FunctionCode +0x1B, decimal 1 771 040). The size of the header supplied by the +caller is taken from *(a1+184)+16 and stored in v8 (renamed v9 in the +patch). + +Pre-patch logic performed only a minimal length test for this control +path: + + if ( IsEnabledDeviceUsage ? v8 < 0x34 : v8 < 0x28 ) // 40 bytes + fail; // rejected + +If the private feature flag was clear (normal systems), a buffer of +only 0x28 (40) bytes satisfied the check. A little later the function +unconditionally accessed fields located at offsets 36, 44 and 48 +inside the same buffer: + + *(v7+36) // QueueTag + *(v7+44) // DataBufferOffset + *(v7+48) // DataBufferLength + +Because the minimum accepted length (0x28) is smaller than the largest +field offset used (0x48), the driver could read or dereference memory +past the end of the allocated SystemBuffer. The buffer is mapped with +user-controlled contents; if it is not resident or is shorter than +required, the kernel dereferences an invalid address and raises a +bugcheck (KERNEL_SECURITY_CHECK_FAILURE / IRQL_NOT_LESS_OR_EQUAL). + +Thus a local, non-privileged process could send a crafted IOCTL with an +undersized SRB_IO_CONTROL header to crash the system. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – undersized header accepted for Function 0x1B +if (!(IsEnabledDeviceUsage ? v15 < 0x34 : v15 < 0x28)) { + // ... + if ((unsigned int)v15 >= (unsigned int)v10) + return v15 < *(unsigned int *)(v7 + 44) + + (unsigned __int64)*(unsigned int *)(v7 + 48) + ? 0xC000000D : 0; // uses offsets 44/48 +} + +// after patch – strict length check +if ((unsigned int)v9 < 0x34) + return 0xC000000D; // STATUS_INVALID_PARAMETER +``` +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens any device that is serviced through StorPort. +2. Send DeviceIoControl containing SRB_IO_CONTROL header: + - HeaderLength = 0x28 (40 bytes) + - Signature = "SCSIDISK" or compatible value + - Function = 0x1B000 (1771040) +3. RaidGetSrbIoctlFromIrp() accepts the request because 0x28 passes the + old length test. +4. Function later reads *(Buffer+0x2C … 0x30) which lies beyond the + 40-byte buffer – page fault in kernel, system crash. + + +Attack Vector +-------------------------------------------------------------------- +Local authenticated attacker sends a crafted IOCTL_SCSI_PASS_THROUGH_* +request to any StorPort-managed device (physical disk, virtual disk, +CD-ROM, etc.). No special privileges beyond the ability to open the +device handle are required. + + +Patch Description +-------------------------------------------------------------------- +The fix places the size validation at the very beginning and enforces a +strict minimum header size of 0x34 (52) bytes for the 0x1B* control +code, removing the earlier feature-flag exception. Subsequent accesses +(v7+36, v7+44, v7+48) are now guaranteed to be within the caller’s +buffer. Additional minor reorderings ensure that all length checks are +performed before any field dereference. + + +Security Impact +-------------------------------------------------------------------- +Before the update a low-privilege user could cause a kernel fault and +system reboot, resulting in a local denial-of-service. No information +leak or code execution was observed from the diff, but the out-of- +bounds access created instability and potential for further +exploitation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new unconditional check (HeaderLength < 0x34) completely blocks the +original under-flow scenario. All later field usages are bounded by +this size, closing the untrusted pointer dereference avenue. No +further unsafe dereferences are visible in the patched routine, so the +repair is considered effective for this code path. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60708_storvsp.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60708_storvsp.sys.txt new file mode 100644 index 0000000..a2e074d --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60708_storvsp.sys.txt @@ -0,0 +1,111 @@ +{'change_count': 3, 'cve': 'CVE-2025-60708', 'file': 'storvsp.sys', 'kb': 'KB5068861', 'confidence': 0.19, 'patch_store_uid': '21cb550f-dde2-4e2c-9107-72cbda61b7e7', 'date': 1763409892.7953272} +-------------------------------------------------------------------- +CVE-2025-60708 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Hyper-V storage virtualization driver +(storvsp.sys) – routines handling VSP SCSI request validation and +VSMB file-open validation. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-822: Untrusted Pointer Dereference (kernel NULL/invalid pointer +access causing denial-of-service). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The structure VSP_REQUEST_PRIVATE (hereafter "REQ") is created by +upper layers and passed to several helper routines prior to being +dispatched to a virtual disk (VDEVICE_OBJECT). Field index 10 of +this structure ( *(QWORD*)REQ + 10 ) is expected to hold a valid +VDEVICE_OBJECT pointer. + +1. In VspIsValidSgRequest(REQ) the original code unconditionally + dereferenced this pointer at offset +2184 to test a flag that + indicates whether the associated virtual disk supports scatter/ + gather (SG) I/O: + *(_BYTE*)( (*(QWORD*)REQ + 10) + 2184 ) + +2. If REQ arrives before a device is associated (the field is NULL) + the read is performed against address 0x000000000000088, causing + a kernel bugcheck (PAGE_FAULT_IN_NONPAGED_AREA). + +3. The pointer originates from user-controlled / guest-controlled + input paths (e.g. IOCTL_VSMB_OPEN_FILE and SCSI SRB processing), + so a malicious caller running with ordinary guest privileges can + craft a request that triggers the dereference. + +4. Similar unchecked uses existed further down the call chain – + VspVsmbFileIoctlVstorVsmbOpenFileValidate() and VspStartJob() – + but these depended on VspIsValidSgRequest() succeeding first, + making the first crash site easy to reach. + +Consequence: Local code running in the guest (or in the root part +of the host that can open the StorVSP device) can reliably crash +the host kernel, resulting in denial of service. No memory-safety +violation beyond the faulting read occurs. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if (!*(_BYTE*)(*((QWORD*)a1 + 10) + 2184) || ... ) + return 0; // unchecked pointer at +10 +``` +```c +// AFTER (simplified) +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() && + !*((QWORD*)a1 + 10)) { + StorVspTrace(..., "No device associated while validating SG request"); + return 0; // bail out before dereference +} +// safe to access *(_QWORD*)a1 + 10 beyond this point +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker issues crafted IOCTL / SCSI SRB to storvsp device. +2. VspStartJob() is invoked -> calls VspIsValidSgRequest(). +3. VspIsValidSgRequest() dereferences REQ->Device (+10) at +2184 + without validity check (pre-patch). +4. NULL pointer access -> bugcheck. + +Attack Vector +-------------------------------------------------------------------- +Any local guest or host process capable of sending virtual-storage +requests to storvsp.sys can supply a SRB whose REQ structure has a +NULL Device pointer, immediately triggering the fault path. +Typical paths: +• IOCTL_VSMB_OPEN_FILE from vmstor-client. +• Hyper-V SCSI passthrough requests. +No special privileges beyond access to the device interface are +required. + +Patch Description +-------------------------------------------------------------------- +1. Added explicit NULL check for REQ->Device before the flag access + in VspIsValidSgRequest(); if NULL the routine now logs and + returns failure. +2. Updated VspVsmbFileIoctlVstorVsmbOpenFileValidate() and + VspStartJob() to adjust trace IDs and to propagate the new + failure path without further dereference. +3. Additional defensive size / range checks were tightened, but the + critical fix is the early NULL-pointer guard. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, an unprivileged local caller could force storvsp.sys to +dereference a NULL function-controlled pointer, immediately causing +a system crash (blue-screen). This yields a reliable local denial +of service on the host. No privilege escalation or data disclosure +was observed. + +Fix Effectiveness +-------------------------------------------------------------------- +The new NULL-pointer guard removes the only path that allowed a NULL +Device pointer to reach the +2184 flag access. Combined with extra +bounds checks in related functions, the issue is effectively +mitigated. A targeted crash is no longer reproducible with a NULL +Device pointer, indicating the patch is effective. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60709_clfs.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60709_clfs.sys.txt new file mode 100644 index 0000000..b81f9bb --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60709_clfs.sys.txt @@ -0,0 +1,123 @@ +{'change_count': 1, 'kb': 'KB5068861', 'date': 1763409819.8502991, 'file': 'clfs.sys', 'cve': 'CVE-2025-60709', 'confidence': 0.24, 'patch_store_uid': 'c23df860-1044-46ff-9a48-1443517129b2'} +-------------------------------------------------------------------- +CVE-2025-60709 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Common Log File System driver (clfs.sys), function +ClfsGetFirstRecord(). The routine is used by several higher-level +CLFS APIs to obtain a pointer to the first log record contained in a +supplied in-memory log container buffer. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read (CWE-125). Insufficient validation of a caller- +controlled offset lets the kernel access memory located outside the +allocated log buffer. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Input parameters + a1 : pointer to an in-memory CLFS container buffer + a2 : length, in bytes, of that buffer + +Internal field + *((uint32_t *)a1 + 10) // offset of the first record, here called + // FirstRecordOffset (FRO) + +Old logic + 1. Read FRO into v2. + 2. Fail if FRO >= 0xFFFFFFD8 (sanity guard against small negative + values once interpreted as signed). + 3. Return &a1[FRO] unless FRO > a2 + 40. + +Problem + The comparison in step 3 is incorrect. The code must ensure that + FRO + sizeof(CLFS_RECORD_HEADER) is still inside the user-supplied + buffer. Instead it only checks FRO against (a2 + 40). Consequently + any value satisfying: + FRO <= a2 + 40 + FRO + 0x28 > a2 + will be accepted. Subsequent code that parses the returned header + will therefore read up to 0x28 bytes after the end of the caller’s + allocation. + + In addition, no lower-bound check on FRO existed. Values below the + minimum legal start offset (0x70) could be supplied, causing the + function to point inside the CLFS super-block where no record really + resides. + + Because CLFS runs in kernel mode, the out-of-bounds access occurs at + elevated privilege and can leak or corrupt kernel memory once other + record-handling helpers treat the bogus pointer as trustworthy. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (simplified) +if (!a1) + return 0; +FRO = *((uint32_t *)a1 + 10); +if (FRO >= 0xFFFFFFD8) + return 0; +if (FRO > a2 + 40) + return 0; +return (CLFS_RECORD_HEADER *)&a1[FRO]; + +// AFTER (simplified) +FRO = *((uint32_t *)a1 + 10); +limit = FRO + 40; // include header size +if (FeatureFlagEnabled) { + if (limit < 0x28 || FRO < 0x70 || limit > a2) + return 0; +} else { + if (limit < 0x28 || FRO > a2 + 40) + return 0; +} +return (CLFS_RECORD_HEADER *)&a1[FRO]; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. A user-mode process causes the kernel to open or append a crafted + CLFS log. +2. clfs.sys allocates a buffer for the container header and calls + ClfsGetFirstRecord(). +3. The attacker-controlled FRO passes the lax checks and points past + the end of the buffer. +4. The returned pointer is dereferenced by record-parsing helpers, + causing an out-of-bounds read (and potentially a write) in kernel + space. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. The attacker provides or modifies a CLFS log +container (for example via public CLFS APIs or by supplying a crafted +.BLF file) and convinces a privileged component to process it. + +Patch Description +-------------------------------------------------------------------- +Microsoft added full range validation: + * Enforces a lower bound on FRO (must be >= 0x70). + * Enforces a minimum record header size (offset+40 >= 0x28). + * Correctly checks that (FRO + 40) does not exceed the caller- + supplied buffer length. + * Introduces feature-flag guarded logic but both paths close the + primitive by requiring offset+header <= buffer. + +Security Impact +-------------------------------------------------------------------- +Before the patch a crafted container could make the kernel read (and +later possibly write) beyond the end of a pool buffer, leading to +information disclosure, memory corruption, and ultimately elevation of +privilege. Successful exploitation yields arbitrary kernel-mode read +and potentially write, allowing a local attacker to execute code with +SYSTEM privileges. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated boundary checks cover both lower and upper bounds and use +inclusive tests on (offset + header_size), removing the off-by-40 +window. No obvious bypass remains in the shown code; therefore the +patch effectively eliminates the described OOB read vector. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60715_rasman.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60715_rasman.dll.txt new file mode 100644 index 0000000..eaf5197 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60715_rasman.dll.txt @@ -0,0 +1,122 @@ +{'kb': 'KB5068861', 'file': 'rasman.dll', 'patch_store_uid': 'dd18337a-8fdc-4617-8570-c6658f6d2e1e', 'date': 1763409826.190237, 'confidence': 0.23, 'cve': 'CVE-2025-60715', 'change_count': 2} +-------------------------------------------------------------------- +CVE-2025-60715 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) +(user-mode module rasman.dll – functions RasPortEnum() and +SubmitRequest()). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / integer-truncation (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RasPortEnum() enumerates modem/port structures that the RRAS +service (via SubmitRequest()) returns to the caller. The caller +supplies two size parameters: + * a3 – total size, in bytes, of the caller-supplied output buffer + * a4 – receives the number of PORT_INFO records that are returned + +Before the patch RasPortEnum() derived three different sizes from +those two values and silently mixed 32-bit and 64-bit arithmetic: + + requestSize = 208 * ( *a3 / 0xD8 ) // 208-byte internal entry + copySize = 216 * ( v26 / 0xD0 ) // 216-byte public entry + allocSize = (unsigned int)requestSize <-- truncated to 32 bit + +1. The 208 and 216 constants do not match. The division rounds + *down*, so requestSize can be smaller than the number of entries + actually returned by the service ( *a4 ). +2. allocSize is truncated to 32 bits and passed to LocalAlloc(). + For large a3 values (>= 0x100000000) the high dword is silently + lost, producing a much smaller allocation than the caller + expects. +3. After the service returns, RasPortEnum() copies + *a4 × 208 bytes from the heap block into the caller’s buffer: + + v22 = &v9[208 * vIndex]; // v9 points to heap block + ... *(_OWORD *)(dest) = *(src) ... + + The loop is bounded only by *a4; it does **not** verify that the + source buffer (v9/allocSize) is large enough. If allocSize is + smaller than *a4×208 the copy continues past the end of the heap + block, corrupting the process heap. + +Because SubmitRequest() is an RPC helper that forwards data to the +remote RRAS service, an authenticated network attacker can control +both *a3 (via the RPC call) and the returned *a4 value, giving him +full control over the overflow size and content. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – RasPortEnum() +requestSize = 208i64 * (*a3 / 0xD8u); // r19 +v9 = LocalAlloc(0x40u, (unsigned int)requestSize); // truncated +... +v18 = 216i64 * (v26 / 0xD0); // bytes needed later +... +while (v8 < *a4) // uses *a4 directly +{ + v22 = &v9[208 * vIndex]; // v9 may be too short + /* 208-byte copy into 216-byte caller buffer */ +} +``` +```c +// after patch – RasPortEnum() +unsigned __int64 tmp = 208i64 * (*a3 / 0xD8u); +if (tmp > 0xFFFFFFFF) return ERROR_INVALID_PARAMETER; // new guard +v9 = LocalAlloc(0x40, (SIZE_T)tmp); +... +if (*a3 < requiredBytes) // new size check + return ERROR_BUFFER_TOO_SMALL; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client invokes RasRpcPortEnum → RRAS service → RasPortEnum(). +2. RasPortEnum() calculates truncated allocSize and allocates heap + buffer v9. +3. SubmitRequest() returns more PORT_INFO records than fit into v9. +4. Copy loop overruns v9 by (*a4×208 – allocSize) bytes, corrupting + heap metadata. +5. Subsequent heap operation results in controlled memory smash → + arbitrary code execution under the RRAS service account + (typically SYSTEM). + +Attack Vector +-------------------------------------------------------------------- +Requires the ability to send RRAS management RPCs (normally +restricted to authenticated users). The attacker supplies an +excessively large a3 value and later forces the service to return a +large *a4, causing the heap overflow inside the RRAS process. + +Patch Description +-------------------------------------------------------------------- +1. Unified the code path – removed feature-flag specific branch. +2. Performed all size computations in 64-bit (unsigned __int64). +3. Added explicit >0xFFFFFFFF check before the value is cast to + 32-bit and passed to LocalAlloc(). +4. Added explicit buffer-size comparison: + if (*a3 < requiredBytes) → ERROR_BUFFER_TOO_SMALL + which aborts before the copy loop. +5. Consistently frees the temporary buffer on all error paths. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an authenticated attacker could reliably overflow +a heap allocation inside the RRAS service and execute arbitrary code +in the context of NT AUTHORITY\SYSTEM, leading to full compromise of +the target server. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code removes the integer truncation, adds 64-bit bounds +checking, and re-checks the caller’s buffer length before copying. +No write occurs unless all size validations succeed, eliminating the +heap overflow condition described above. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60716_dxgkrnl.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60716_dxgkrnl.sys.txt new file mode 100644 index 0000000..d73aca6 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60716_dxgkrnl.sys.txt @@ -0,0 +1,128 @@ +{'confidence': 0.25, 'patch_store_uid': 'ef35a2f2-7561-402f-9995-40a114a8900a', 'file': 'dxgkrnl.sys', 'date': 1763409983.1218996, 'kb': 'KB5068861', 'cve': 'CVE-2025-60716', 'change_count': 67} +-------------------------------------------------------------------- +CVE-2025-60716 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows DirectX graphics kernel driver (dxgkrnl.sys), +function DXGDEVICESYNCOBJECT::DestroyCoreState. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (kernel-mode privilege escalation). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +DestroyCoreState tears down several scheduler-side and video memory +objects that belong to a DXGDEVICESYNCOBJECT instance. When the flag +bit held in the object at offset 0x5C ( ``(*((_BYTE *)this+92)&1)`` ) +is set, the routine obtains a pointer to the scheduler sync object by +calling GetVidSchSyncObject and then calls a driver supplied destroy +callback located at: + (*(void**)(VidSchSyncObjectVtable)+0x2A8) + +Pre-patch, no check verified that the returned pointer was still +valid. In several situations the pointer may already have been freed +or set to NULL: + 1. The adapter render object carries byte flag 0x209 signalling that + scheduler teardown has already happened. + 2. Another thread running Feature_H2E_WPA3SAE_93 flow may destroy the + object first. + +If either case occurs, DestroyCoreState dereferences a stale pointer +and issues a virtual call, corrupting arbitrary kernel memory. +Because the pointer contents are attacker controlled pool memory, the +fault can be turned into arbitrary code execution inside the graphics +kernel, yielding SYSTEM privileges. + +The defect is therefore a classic UAF caused by a missing liveness +check and by executing teardown logic after the associated adapter has +already released its core resources. + +Structures / fields involved + ADAPTER_RENDER +0x209 : SchedulerReleased flag (boolean) + DXGDEVICESYNCOBJECT +0x5C: InCoreState flag bit 0 (0x1) + Pointer to VID scheduler sync object stored inside the device sync + object (returned by GetVidSchSyncObject). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch +VidSchSyncObject = DXGDEVICESYNCOBJECT::GetVidSchSyncObject(this); +v6 = *(_QWORD *)(*(_QWORD *)(v5 + 16) + 736i64); +v7 = *(_QWORD *)(v6 + 8); +LOBYTE(v6) = (*((_DWORD *)this + 18) & 0x420) == 0; +(*(void (__fastcall **)(struct _VIDSCH_SYNC_OBJECT *, _QWORD, __int64, + _QWORD))(v7 + 680))( + VidSchSyncObject, // may be freed + *(_QWORD *)(v5 + 800), + v6, + *((_QWORD *)this + 3)); // use-after-free +``` +```c +// after patch (trimmed) +IsEnabledDeviceUsage_93 = Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_93(); +if (IsEnabledDeviceUsage_93) { + VidSchSyncObject = DXGDEVICESYNCOBJECT::GetVidSchSyncObject(this); + if (!VidSchSyncObject) // new NULL check + goto FreeLocalState; +} +VIDSCH_EXPORT::VidSchDestroyDeviceSyncObject( + SchedulerExport, + VidSchSyncObject, + VidSchDevice, + (Flags & 0x420) == 0, + RegisteredCallback); +FreeLocalState: + ... // safe local free +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User mode opens a D3DKMT device and creates a native fence / device + sync object. +2. Through multithreaded or race operations the adapter core is torn + down, clearing the scheduler object and setting adapter flag 0x209. +3. A final reference triggers DXGDEVICESYNCOBJECT::DestroyCoreState. +4. Legacy code skips the flag check, retrieves a stale pointer and + calls into freed memory –> pool corruption –> EoP. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker invokes crafted DirectX (D3DKMT) API +sequences to race object creation/destruction, forcing the graphics +kernel to call DestroyCoreState after the scheduler object is already +freed. + +Patch Description +-------------------------------------------------------------------- +1. Early exit: if adapter->CoreReleasedFlag (byte +0x209) is set the + function returns immediately, avoiding any interaction with freed + scheduler objects. +2. Introduced feature gate + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_93(). + When the gate is on, the code now verifies that + GetVidSchSyncObject() returns a non-NULL pointer before use. +3. Replaced manual virtual-table call with + VIDSCH_EXPORT::VidSchDestroyDeviceSyncObject, consolidating + validation in a single helper. +4. Log line numbers bumped (+1) to match new source. + +Security Impact +-------------------------------------------------------------------- +An attacker can obtain arbitrary kernel read/write and execute code in +ring-0, thereby escalating from a standard user to SYSTEM. The bug is +triggerable from a standard desktop session and does not require admin +rights or physical access. + +Fix Effectiveness +-------------------------------------------------------------------- +The added adapter flag guard removes the path that allowed reuse after +scheduler teardown. The extra NULL pointer check and the use of a +validated helper API eliminate the direct dereference of stale memory. +No remaining call sites of GetVidSchSyncObject inside DestroyCoreState +operate without validation. Therefore the patch appears to close the +UAF; comprehensive fuzzing would be required to rule out parallel free +scenarios in other code paths (unknown). diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60717_bcastdvruserservice.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60717_bcastdvruserservice.dll.txt new file mode 100644 index 0000000..6916a07 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60717_bcastdvruserservice.dll.txt @@ -0,0 +1,147 @@ +{'patch_store_uid': 'a5d1cc68-4205-440d-bca0-8dbbfd6959b5', 'kb': 'KB5068861', 'confidence': 0.15, 'cve': 'CVE-2025-60717', 'file': 'bcastdvruserservice.dll', 'change_count': 9, 'date': 1763412270.7896793} +-------------------------------------------------------------------- +CVE-2025-60717 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Broadcast DVR User Service (bcastdvruserservice.dll), mainly +class Windows::Media::Capture::Internal::BroadcastManager and helper +client classes used by the Game DVR / App Broadcasting feature set. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416 – Use After Free caused by a missing synchronization (thread +safety / race condition) on reference-counted interface pointers that +are shared across multiple threads inside the service. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Broadcast Manager maintains several COM interface pointers that are +stored directly in class members: + + offset +0x1A0 (member 52) – IUriRuntimeClass (OAuth callback URI) + + offset +0x1A8 (member 53) – IWebAuthenticationResult (OAuth result) + + additional members at +0x198/+0x1A0 accessed during + HandleExitBroadcastMode. + +Prior to the patch the following code paths accessed these members +without any mutual exclusion: + • SetOAuthCallbackUri(..) / SetAuthenticationResult(..) replaced the + stored pointer and released the previous interface. + • GetOAuthCallbackUri(..) / GetAuthenticationResult(..) returned the + pointer to callers and performed AddRef after the load. + • HandleExitBroadcastMode(..) asynchronously cleared the same members + while other threads could still call the getters. + +Because the load-AddRef sequence in the getters was not protected, the +following inter-thread race was possible: + 1. Thread A executes GetOAuthCallbackUri() + ptr = m_pCallbackUri; // load + 2. Thread B pre-empts, calls SetOAuthCallbackUri(newUri); + Release(oldPtr); // decrements ref-count + m_pCallbackUri = newUri; // store + if (oldPtr->ref == 0) free(oldPtr) + 3. Thread A resumes, executes AddRef(ptr) on the freed object. + +The same pattern exists for IWebAuthenticationResult and for the +HandleExitBroadcastMode clean-up routine. A stale pointer dereference +leads to heap use-after-free, memory corruption and ultimately privilege +escalation inside the SYSTEM service process. + +Affected structures / parameters + • BroadcastManager::m_pOAuthCallbackUri (member 52) + • BroadcastManager::m_pAuthenticationResult (member 53) + • Parameter a2 of setters / double-pointer a2 of getters + • Critical section added at offset +0x3C8 (field starts at +968) + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Pre-patch (no locking): +```c +// SetOAuthCallbackUri - before +if (a2) { + if (this->m_pOAuthCallbackUri != a2) { + a2->AddRef(); + auto old = this->m_pOAuthCallbackUri; + this->m_pOAuthCallbackUri = a2; + if (old) old->Release(); // releases while getters run + } +} +``` + +Patch (critical section added): +```c +if (Feature2098154810_IsEnabled()) { + EnterCriticalSection(this + 968); + if (this->m_pOAuthCallbackUri != a2) { + a2->AddRef(); + auto old = this->m_pOAuthCallbackUri; + this->m_pOAuthCallbackUri = a2; + if (old) old->Release(); + } + LeaveCriticalSection(this + 968); +} +``` + +Similar changes were applied to GetOAuthCallbackUri / (Authentication +Result) and to HandleExitBroadcastMode, which now takes the same lock +before clearing the members. + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged client rapidly issues + BroadcastManager::SetOAuthCallbackUri() from one thread while + simultaneously issuing GetOAuthCallbackUri() from another thread. +2. Race window between load and AddRef in the getter collides with the + Release in the setter. +3. Freed COM object vtable is dereferenced inside the SYSTEM service. +4. Memory corruption → controlled code execution in the service → local + elevation of privilege. + + +Attack Vector +-------------------------------------------------------------------- +Local. Any sandboxed or low-privileged Windows Store application that +has access to the public App Broadcasting APIs can interact with the +Broadcast DVR User Service and provoke concurrent setter / getter calls +needed for the race. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced two feature-flag guarded helper classes + (Feature 2098154810 and Feature 75275577) whose + GetCachedFeatureEnabledState() functions were added. +2. Added a dedicated critical section at offset +968 in + BroadcastManager. All accesses that load, store, or clear the + vulnerable interface members are now wrapped with + EnterCriticalSection / LeaveCriticalSection when either new feature + is enabled. +3. HandleExitBroadcastMode was rewritten to take the same lock before it + nulls the interface pointers, eliminating another unsynchronized + Release path. +4. Minor type cleanup (a3 changed to unsigned) and additional defensive + checks. + + +Security Impact +-------------------------------------------------------------------- +Prior to the update a local attacker could reliably trigger a heap +use-after-free inside a SYSTEM service, leading to arbitrary code +execution in the service context and therefore elevation of privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added critical section provides mutual exclusion, making the +load-AddRef and store-Release sequences atomic with respect to each +other; thus the stale pointer race is removed. Because the lock is used +consistently in all getters, setters, and shutdown paths, no obvious +remaining UAF window was observed in the patched diff. The mitigation +is however gated behind runtime feature flags; the fix is effective only +when the corresponding features are enabled on the target build. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60718_consent.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60718_consent.exe.txt new file mode 100644 index 0000000..ea2ae4b --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60718_consent.exe.txt @@ -0,0 +1,122 @@ +{'patch_store_uid': '6e89b61c-bd67-4d2a-9782-fd415c123ea5', 'date': 1763409824.1461997, 'confidence': 0.12, 'change_count': 7, 'kb': 'KB5068861', 'file': 'consent.exe', 'cve': 'CVE-2025-60718'} +-------------------------------------------------------------------- +CVE-2025-60718 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +consent.exe (UAC / AppInfo – Administrator-Protection logic) +The vulnerable code sits in WinMain() and supporting WIL telemetry +helpers contained in consent.exe. + + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted search path / DLL-search-order hijacking (CWE-426) +This is an Elevation-of-Privilege flaw that lets a local, already +signed-in attacker plant a rogue DLL that will be loaded by the highly +privileged consent.exe process. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. consent.exe is started by the AppInfo service whenever UAC + elevation is requested. Because the binary runs as high-integrity + it must prevent loading modules from attacker-controlled paths. +2. Microsoft uses PROCESS_MITIGATION_IMAGE_LOAD_POLICY with the flag + PreferSystem32Images=1 to make the loader always search %SystemRoot% + \System32 first, thereby neutralising classic DLL planting attacks. +3. In the unpatched WinMain the mitigation was only applied through + the following sequence: + + if (Feature_ShadowAdmin_IsEnabled()) <-- A + SetProcessMitigationPolicy(ImageLoadPolicy,1); <-- B + + a) The feature gate in line A only checked the regular + ShadowAdmin feature bit. + b) When the feature was **disabled** (standard client & many server + SKUs), line B never executed and no image-load policy was set. +4. Once the policy is missing, the default Windows search order + (current directory – PATH – System32) is used. During early + start-up consent.exe implicitly loads several DLLs (faultrep.dll, + Version.dll, UXTHEME.dll …) by name only. If an attacker is able to + place a spoofed copy in the current directory he gains code + execution inside the high-integrity consent.exe context and can + elevate privileges. +5. Nothing else in the binary locked down the search path, therefore + exploitation is fully local and only requires the user to trigger a + UAC prompt from a writable directory (e.g. the attacker’s own + Downloads folder). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// WinMain – BEFORE (excerpt) +if (wil::Feature<ShadowAdmin>::IsEnabled()) { + ReturnLength = 1; + SetProcessMitigationPolicy(8, &ReturnLength, 4); // may be skipped +} +``` +```c +// WinMain – AFTER (excerpt) +if (wil::Feature<ShadowAdmin>::IsEnabled()) { + int flags = 0; + if (Feature_ShadowAdmin__private_IsEnabledDeviceUsageNoInline() && + RtlQueryElevationFlags(&flags) >= 0 && (flags & 0x18) == 0x10) { + ReturnLength = 1; + SetProcessMitigationPolicy(8, &ReturnLength, 4); // always hit + } +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User executes a program from an attacker-writable directory. +2. Program requests elevation –> service launches consent.exe. +3. ShadowAdmin feature is disabled –> image-load mitigation **not** set. +4. Loader resolves implicit DLL import; current directory searched + before System32. +5. Attacker-planted DLL is found and executed with elevated rights. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker places a malicious DLL (matching any +imported module name) in the working directory and then triggers a UAC +prompt from that same location. When consent.exe starts it loads the +malicious DLL and the attacker gains high-integrity code execution. + + +Patch Description +-------------------------------------------------------------------- +• Introduced helper Feature_ShadowAdmin__private_IsEnabledDeviceUsage + NoInline() and an additional call to RtlQueryElevationFlags(). +• The result gates the call to + SetProcessMitigationPolicy(PROCESS_MITIGATION_IMAGE_LOAD_POLICY) + guaranteeing that PreferSystem32Images=1 is applied whenever the + Administrator-Protection feature is in use on a device that supports + it. +• Ancillary clean-ups in several GetCurrentFeatureEnabledState() + helpers do not affect the vulnerability but were refactored while + touching the same code region. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could reliably load an arbitrary DLL +into a high-integrity consent.exe process, thereby escaping the current +integrity level and achieving local elevation of privilege. The fix +removes the current-directory element from the search order, closing +this classic DLL planting vector. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched binary unconditionally sets PreferSystem32Images before any +optional module is loaded, and the check is performed at the very start +of WinMain. No further calls that could revert the mitigation were +observed. Therefore the repair is complete and the untrusted search +path is eliminated. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60719_afd.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60719_afd.sys.txt new file mode 100644 index 0000000..4fb6157 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60719_afd.sys.txt @@ -0,0 +1,135 @@ +{'cve': 'CVE-2025-60719', 'date': 1763412331.5615878, 'change_count': 5, 'file': 'afd.sys', 'patch_store_uid': 'dd9c6721-2756-4ad6-92a0-850c704dd674', 'kb': 'KB5068861', 'confidence': 0.19} +-------------------------------------------------------------------- +CVE-2025-60719 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) – several +request-handling routines (AfdSocketTransferBegin / End, AfdBind, +AfdConnect and AfdGetInformation). + + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted pointer dereference (CWE-822) that can be triggered from +user-mode through crafted Winsock/AFD ioctl sequences, resulting in +local elevation of privilege. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. All affected entry points retrieve structures that originate in + user space (socket-address buffers, OPTION structs, IRP context or + FILE_OBJECT pointers supplied through handles). + +2. In the pre-patch code these user controlled addresses are copied or + dereferenced in kernel mode without first: + • verifying the caller supplied length against the structure + actually referenced; or + • pinning / probing the pages; or + • preventing the underlying endpoint from being “un-bound” while + the pointers are being used. + +3. Example – AfdConnect (x86 paths omitted for brevity): + a. The user buffer is parsed: + Src = *(char **)(irp + 0x20) + 0xC; + Size = *(DWORD *)(irp + 0x10) – 0xC; + b. Kernel buffer ‘Pool2 + 0x60’ is initialised and the *pointer* + from user land is blindly copied with + memmove(Pool2 + 0x60, Src, Size); + c. Downstream helpers (e.g. AfdTdi_TA6toTA4_InPlace, + AfdSetupConnectDataBuffers) treat the embedded fields as + trusted, leading to arbitrary kernel dereference when the + caller supplies a fake pointer value inside the copied buffer. + +4. AfdBind, AfdSocketTransferBegin and AfdSocketTransferEnd follow the + same pattern when building MDLs or when sending TDI requests – the + endpoint can be unbound while the request is in-flight, turning the + once-valid DeviceObject / FileObject pointers into attacker + controlled memory. + +5. Because the code runs in kernel context, a crafted pointer can be + used to gain read/write access to arbitrary kernel memory and thus + to execute code at ring-0. + +Structures / fields involved (64-bit offsets): + ENDPOINT +0x18 Socket state flags + ENDPOINT +0x28 FileObject pointer (user supplied handle path) + IOCTL_IN +0x00 AF_UNIX/SOCKADDR header copied via memmove() + +A mismatch between the user supplied size and the real structure +results in the dereference of attacker chosen addresses. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// AfdConnect – before patch +Pool2 = ExAllocatePool2(97, userLen+0x60, TAG); +memmove((void*)(Pool2+0x60), userPtr, userLen); +... +AfdTdi_TA6toTA4_InPlace(v58[2].Buffer, &v58[2]); // uses data just + // copied from user +``` +```c +// AfdBind – before patch +P = ExAllocatePool2(97, len, TAG); +memmove(P, userSockAddr+4, len); // no probe / validation +``` +```c +// AfdSocketTransferBegin – before patch +_DeviceObject = *(PDEVICE_OBJECT *)(Endpoint+0x28); +_FileObject = *(PFILE_OBJECT *)(Endpoint+0x18); +IofCallDriver(_DeviceObject, Irp); // possible stale / attacker ptr +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process obtains an AFD handle (AFD_CREATE). +2. Crafts an ioctl buffer that contains a fake kernel pointer in one of + the embedded fields (e.g. sockaddr structure). +3. Sends the buffer with NtDeviceIoControlFile → reaches one of the + vulnerable routines. +4. Kernel copies the buffer and later dereferences the embedded pointer + while still running at IRQL <= APC_LEVEL. +5. Controlled kernel read/write → privilege escalation. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user; no special privileges required other than +ability to open an AFD device handle (granted to normal users). + + +Patch Description +-------------------------------------------------------------------- +The fix introduces defensive checks and state balancing: +1. Strict length / AF checks before copying user buffers. +2. KeGetCurrentIrql() + RtlEqualUnicodeString() guard to ensure only + Bluetooth RFCOMM endpoints are handled by the fast-path. +3. Feature-flag gated call to AfdPreventUnbind() with a matching + AfdReallowUnbind() in every exit path so the endpoint cannot be + detached while pointers are in use. +4. Additional checks on ENDPOINT type, address family, socket state and + MDL allocation success; immediate bailout with STATUS_INVALID_PARAM + on failure. +5. Output / user buffers are now written through an explicit probe and + volatile copy (RtlCopyVolatileMemory). + + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could cause afd.sys to dereference an +attacker-controlled kernel pointer leading to arbitrary kernel memory +access and full elevation to SYSTEM. + + +Fix Effectiveness +-------------------------------------------------------------------- +All dereferences are now protected by size/AF validation, IRQL checks, +proper probing of user addresses and by holding the unbind lock around +sensitive sections. Each exit path re-enables unbind, eliminating the +leak. No further untrusted pointer paths were observed in the updated +functions, so the vulnerability is considered fixed. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60721_consent.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60721_consent.exe.txt new file mode 100644 index 0000000..71f7df0 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60721_consent.exe.txt @@ -0,0 +1,151 @@ +{'kb': 'KB5068861', 'cve': 'CVE-2025-60721', 'file': 'consent.exe', 'confidence': 0.27, 'change_count': 7, 'date': 1763409997.315364, 'patch_store_uid': '6e89b61c-bd67-4d2a-9782-fd415c123ea5'} +-------------------------------------------------------------------- +CVE-2025-60721 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +consent.exe – Windows Administrator Protection (WAP) user-mode helper +that brokers elevation/UAC prompts. All code shown is inside the +binary’s feature-gating helpers (wil::details::FeatureImpl<...>), its +ReportUsage telemetry helper and WinMain – the main entry point that +switches process context and applies mitigation policies. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-270: Privilege Context Switching Error (local elevation of +privilege caused by wrong security-state decision logic). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. At process start WinMain determines whether the optional WAP + security hardening (internally “Feature_ShadowAdmin” / feature id + 2578215227) must be applied. In the vulnerable build the code: + + if (Feature_ShadowAdmin_IsEnabled()) + SetProcessMitigationPolicy(8, &ReturnLength, 4); + + executed unconditionally once the feature bit was set. No further + validation of the caller’s actual elevation state was performed. + An attacker running inside an already elevated medium-IL process + could therefore launch consent.exe with crafted command-line + parameters that force WAP into an incorrect context-switch path + later in WinMain. Because the mitigation policy was enabled even + when the process was started in the wrong elevation context, + subsequent privilege checks relied on bad assumptions and the + token handling code (ImpersonateLoggedOnUser / RevertToSelf + blocks) was executed while the process still owned high-privilege + handles, letting the attacker escape back to a SYSTEM / high-IL + token that is written into the target process memory (NtWrite­ + VirtualMemory path around line 6100 in the original WinMain). + +2. Several wil::details::FeatureImpl<...>::GetCurrentFeatureEnabled­ + State helpers generated the per-feature state bitmask that drives + the above decision. Before the patch those helpers used a + complicated flag-merge routine that left bit-0 (the final + “valid/usable” flag) set even when the feature’s usage report could + not be trusted (variable v10 remained zero, but v11 kept the value + 1). The patched version rewrites the routine and explicitly + forces bit-0 to be cleared unless both the 0x40 (valid) flag and a + successful ReportUsage() call are present, closing the logic hole. + +3. wil::details::FeatureImpl<...>::ReportUsage previously accepted an + attacker-controlled byte (a2) that was forwarded to + ReportUsageToService as the ReportingKind parameter. The patch + turns that into a fixed constant (1) and changes the prototype so + untrusted data can no longer steer the reporting path that feeds + back into GetCurrentFeatureEnabledState. + +4. Finally, WinMain now performs two new validations before calling + SetProcessMitigationPolicy: + a. Feature_ShadowAdmin__private_IsEnabledDeviceUsageNoInline() + must return true (device-specific gating). + b. RtlQueryElevationFlags(&Flags) must succeed and the returned + flags must indicate “ELEVATED_AND_NOT_VIRTUALIZED” (bit 0x10). + + Only when both hold will the mitigation be applied, preventing the + confused-deputy scenario triggered by a low-privilege caller. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// WinMain (before) +if (Feature_ShadowAdmin_IsEnabled()) { + ReturnLength = 1; + SetProcessMitigationPolicy(8, &ReturnLength, 4); // no validation +} + +// WinMain (after) +if (Feature_ShadowAdmin_IsEnabled()) { + DWORD Flags = 0; + if (Feature_ShadowAdmin_IsEnabledDeviceUsageNoInline() && + RtlQueryElevationFlags(&Flags) >= 0 && (Flags & 0x18) == 0x10) + { + ReturnLength = 1; + SetProcessMitigationPolicy(8, &ReturnLength, 4); + } +} + +// ReportUsage (before) +__int64 ReportUsage(_DWORD *state, unsigned __int8 kind, ...) +{ + ... + return ReportUsageToService(state+2, 54237988i64, bit10, bit11, + &v9, kind, 0); +} + +// ReportUsage (after) +__int64 ReportUsage(unsigned int *state, __int64 /*unused*/, ...) +{ + ... + return ReportUsageToService(state+2, 54237988i64, bit10, bit11, + &v8, 1, 0); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker launches consent.exe with crafted command line that refers + to a privileged target process and memory address (arguments are + parsed around WinMain line 240). +2. Feature_ShadowAdmin is enabled; before the patch the process always + calls SetProcessMitigationPolicy, putting consent.exe into an + inconsistent state regardless of real elevation flags. +3. Later, NtDuplicateObject / NtWriteVirtualMemory writes an elevated + token handle into the target process (lines 6100-6180). Because + the earlier context checks were bypassed, this succeeds and the + attacker obtains a high-privilege token. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker starts consent.exe with crafted +parameters while running under a medium-IL session that already has +some administrative privileges (e.g., via scheduled task). + +Patch Description +-------------------------------------------------------------------- +1. Strengthened feature state calculation – mandatory usage reporting + must succeed before bit-0 (“state valid”) is set. +2. ReportUsage no longer accepts caller-supplied ReportingKind. +3. WinMain now checks both device-usage and elevation flags before + invoking SetProcessMitigationPolicy, preventing the faulty context + switch that kept elevated handles alive. +4. Numerous trace-guid and cosmetic renames; not security-relevant. + +Security Impact +-------------------------------------------------------------------- +Before the fix a standard user who could already run code in an +administrative context could coerce consent.exe into re-using elevated +handles after RevertToSelf(), allowing the write of an arbitrary token +handle into another process and ultimately elevating to SYSTEM / full +administrator locally. No user interaction beyond launching the tool +was required. + +Fix Effectiveness +-------------------------------------------------------------------- +All introduced checks are performed in the same early code path that +previously mis-configured the process. The additional elevation flag +validation, the hardened feature state logic, and the constant +ReportingKind together remove the incorrect privilege transition path. +Static review of the patched code shows no remaining way to reach the +unsafe NtWriteVirtualMemory block with an untrusted token. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60721_peauth.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60721_peauth.sys.txt new file mode 100644 index 0000000..747c9ff --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60721_peauth.sys.txt @@ -0,0 +1,126 @@ +{'cve': 'CVE-2025-60721', 'file': 'peauth.sys', 'change_count': 2, 'kb': 'KB5068861', 'confidence': 0.15, 'date': 1763409834.6257224, 'patch_store_uid': '41b13c75-ed0a-4f1b-9b2e-0cb89f0cc6c9'} +-------------------------------------------------------------------- +CVE-2025-60721 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +peauth.sys (Windows Administrator Protection driver) +Affected +routine: SymCryptRsaPkcs1ApplySignaturePadding +(sub_14000A1E8) and associated higher-level caller +sub_14000B710. + +Vulnerability Class +-------------------------------------------------------------------- +Memory corruption / buffer overflow caused by insufficient argument +validation (CWE-120 / CWE-131). Public bulletin lists it under +"Privilege Context Switching Error" but the observable root cause is +a kernel-mode write beyond the supplied output buffer. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper routine SymCryptRsaPkcs1ApplySignaturePadding is used by the +PEAuth driver to build PKCS#1 v1.5 signature blocks inside a caller +supplied buffer (argument a6, size a7). + +1. Before the fix the function only rejected unexpected flag bits and + performed the check + if (v11 > 0x80 || v11 + 11 > a7) return 32782; + where v11 is the computed payload length. No limit existed for the + original message length (a2) with respect to the destination size + a7, so a2 could be chosen equal to a7 or larger, making the final + memmove() copy overlap or run past the end of the caller supplied + buffer. + +2. In addition, the combination logic for optional parameters a3/a4 + (ASN.1 encoded algorithm identifier) accepted the illegal state + "a3 == NULL && a4 != 0" or "a3 != NULL && a4 == 0". For the latter + case v11 was calculated as a2 + 6 even though nothing was actually + copied from a3, shifting every subsequent write six bytes further + than intended. + +3. Caller sub_14000B710 allocates several temporary buffers and then + re-uses the padded signature blob produced by + SymCryptRsaPkcs1ApplySignaturePadding. Because the routine could + overrun its output, adjacent heap arenas allocated through + SymCryptCallbackAlloc are corrupted. The corrupted data are later + interpreted as big-integer limbs or EC points, ultimately allowing a + local attacker to pivot the execution flow inside the kernel and + run arbitrary code with SYSTEM privileges. + +Affected structures / parameters + a1 : pointer to message to be signed (user controlled) + a2 : length of message (user controlled) + a3 : optional algorithm identifier + a4 : length of a3 + a5 : option flags (only bit 0 valid) + a6 : kernel buffer supplied by caller of peauth.sys + a7 : size of a6 in bytes + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old code – missing upper bound for a2 +if ( (a5 & 0xFFFFFFFE) != 0 ) + return 32782; +... +if ( v11 > 0x80 || v11 + 11 > a7 ) + return 32782; +... +memmove(v14, a1, a2); // may write past end of buffer a6 +``` +```c +// Patched code – extra checks +if ( (a5 & 0xFFFFFFFE) != 0 || a2 >= a7 ) + return 32782; // new size gate +... +if ( v11 > 0x80 ) + return 32782; +if ( v11 + 11 > a7 ) + return 32782; // split for clarity +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode -> IOCTL to peauth.sys -> sub_140044E74 … -> +sub_14000A1E8 (build padding) -> corrupted heap in +sub_14000B710 -> subsequent big-integer operations use corrupted +memory -> arbitrary kernel memory overwrite / EOP. + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated attacker sends a crafted request that reaches the +peauth.sys signature-creation path (e.g., through the Windows +Administrator Protection service). By selecting + a2 >= a7 and mismatching a3/a4 parameters +he forces SymCryptRsaPkcs1ApplySignaturePadding to write beyond the end +of the kernel buffer, leading to privilege escalation to SYSTEM. + +Patch Description +-------------------------------------------------------------------- +1. Added explicit size gate "a2 >= a7". +2. Separated the two length tests to guarantee both conditions are + evaluated after v11 is known. +3. Re-worked the a3/a4 decision table: only exact (ptr+len) pairs are + accepted; any mismatch now returns 32782. +4. Caller function migrated to safer helper routines + (sub_1400118A0, sub_14000B650, sub_14000AC20, etc.) and changed + several magic parameters from 0 to 1, indicating hardened + semantics. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, an authorized but unprivileged local user could +corrupt kernel heap structures and execute arbitrary code in kernel +context, resulting in a full privilege escalation to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The additional bounds check ensures that the source length (a2) can no +longer exceed the destination, and the stricter parameter validation +eliminates inconsistent (a3/a4) combinations. Together with the switch +to hardened helper functions in the caller, the patch fully prevents the +observed overflow condition. No residual write past end-of-buffer path +could be identified during diff analysis, though a comprehensive audit +of surrounding SymCrypt helpers is still recommended. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60723_dxgkrnl.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60723_dxgkrnl.sys.txt new file mode 100644 index 0000000..84392a5 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60723_dxgkrnl.sys.txt @@ -0,0 +1,143 @@ +{'cve': 'CVE-2025-60723', 'patch_store_uid': 'ef35a2f2-7561-402f-9995-40a114a8900a', 'date': 1763412355.174791, 'file': 'dxgkrnl.sys', 'kb': 'KB5068861', 'confidence': 0.16, 'change_count': 67} +-------------------------------------------------------------------- +CVE-2025-60723 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows DirectX Graphics Kernel (dxgkrnl.sys). The faulty +logic resides in the display-mode management and logical-allocator +helpers: + • SmmCreateLogicalAllocator() + • DxgkpGetDisplayModeList() + • CheckMultiPlaneOverlayInternal3() + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition resulting in CWE-416: Use-After-Free. Missing +synchronisation around shared adapter data lets two or more threads +free / reuse the same structures concurrently, leading to kernel +memory corruption and a system crash (DoS). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Shared state + All three routines operate on per-adapter global arrays that describe + display modes, logical memory blocks and overlay planes. The arrays + are pointed to from adapter memory that is reachable from *any* + graphics client running on the machine. + +2. Missing lock (DxgkpGetDisplayModeList) + Before the patch the function only held a COREADAPTERACCESS lock. + While that lock is dropped the code recursively re-enters + DxgkpGetDisplayModeList for other adapters, frees the current + cached mode list and reallocates a new one, then later continues to + dereference the stale pointer. Concurrent threads can therefore + UAF the cached list. + +3. Use of uninitialised order (SmmCreateLogicalAllocator) + The first call to + SmmGetOrderBlockSizeInPages(v17) + uses v17 before it is initialised, causing an incorrect decrement + of the page counter. The loop may over-iterate, write past the end + of the newly allocated 1 328-byte structure and corrupt the linked + list that is shared by other threads. + +4. Cascade into CheckMultiPlaneOverlayInternal3 + The overlay helper consumes the same global lists without any + additional serialisation. A simultaneous free in the mode-list + path can therefore leave dangling plane pointers that are walked + later in the function, again crashing the kernel. + +5. Result + A user-mode process can race two threads that call the affected + D3DKMT ioctls (GetDisplayModeList, CommitVidPn, overlay checks, …) + and trigger a BSOD (KERNEL_MODE_EXCEPTION / BUGCHECK 0x3B or 0x50). + No privileges beyond graphics-device access are needed. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (missing lock and uninitialised variable): +```c +// DxgkpGetDisplayModeList (excerpt) +CachedModeList = ADAPTER_DISPLAY::GetCachedModeList(a1[390], a4); +// no push-lock taken – other thread can free the list here +... +for ( i = v7; i; i -= SmmGetOrderBlockSizeInPages(v17) ) +{ // v17 is garbage on 1st iteration + ... +} +``` +After (fixed): +```c +// Take shared push-lock that covers the cached list +if (Feature_IsEnabledDeviceUsage_8()) { + lock = AutoPushLock; + lock->Address = Base + 1128; + DXGAUTOPUSHLOCK::AcquireShared(lock); +} +... +while (v8) { + Lower = SmmGetLowerOrderFromPageCount(v8); + v8 -= SmmGetOrderBlockSizeInPages(Lower); // now valid +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode calls D3DKMTGetDisplayModeList (win32k → dxgkrnl). +2. DxgkpGetDisplayModeList() allocates / frees the adapter’s cached + mode list without holding a global push-lock. +3. A second thread enters the same ioctl and frees the list while the + first thread still holds a pointer. +4. First thread proceeds, dereferences the freed memory in + CheckMultiPlaneOverlayInternal3() or during list iteration and + crashes the kernel. + + +Attack Vector +-------------------------------------------------------------------- +An authenticated local user spawns two (or more) threads that +aggressively call the public D3DKMT* ioctls in parallel. Timing the +calls forces the race and causes a system-wide denial of service. No +special privileges are required beyond the ability to open a graphics +adapter handle (granted to ordinary users). + + +Patch Description +-------------------------------------------------------------------- +• Introduced DXGAUTOPUSHLOCK in DxgkpGetDisplayModeList. The lock is + acquired in shared mode for reads and upgraded to exclusive before + mode-list replacement, preventing concurrent frees. +• Added feature-flag gates (IsEnabledDeviceUsage_xx) so the new locking + is only active on patched systems. +• Fixed uninitialised variable in SmmCreateLogicalAllocator and + increased the allocation size from 1 328 to 1 336 bytes to match the + extended structure. +• Re-implemented CheckMultiPlaneOverlayInternal3 to use typed + PagedPoolZeroedArray helpers, removing the complex manual allocation + logic and guaranteeing single-owner freeing. +• Added extra parameter validation (e.g., max address-space and page + count checks) to refuse oversize requests early. + + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could reliably crash the kernel, leading to +local denial of service. Because the bug is a classic UAF in kernel +context, elevation of privilege might also be possible, but no exploit +for that has been demonstrated. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added push-lock enforces single-threaded updates to the shared +mode-list, eliminating the double-free / UAF window. The allocator +loop now uses a fully initialised order variable and correct page +count arithmetic, removing the out-of-bounds write. Combined with the +new structured pool helpers and early-reject guards, the vulnerability +appears fully addressed. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60724_gdiplus.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60724_gdiplus.dll.txt new file mode 100644 index 0000000..79633be --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-60724_gdiplus.dll.txt @@ -0,0 +1,135 @@ +{'patch_store_uid': '4e9b8fa4-bb62-4ecd-8978-b314a888dcd5', 'confidence': 0.11, 'date': 1763409955.261717, 'kb': 'KB5068861', 'cve': 'CVE-2025-60724', 'file': 'gdiplus.dll', 'change_count': 9} +-------------------------------------------------------------------- +CVE-2025-60724 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft GDI+ (gdiplus.dll) – region processing and scan–bitmap +blending routines (DpRegion::ValidateAndSet, EpScanBitmap::NextBuffer +and EpScanBitmap::NextBufferWithBounds). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / integer-handling error (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. EpScanBitmap::NextBufferWithBounds originally declared its fifth + parameter (pixel count / span width) as unsigned int. When the + caller supplied a negative size the value was implicitly converted + to a large positive 32-bit integer. + +2. The helper EpScanBitmap::NextBuffer receives this value (a5) as a + signed int, but immediately stores it in an unsigned local + variable (v7). Therefore a negative span (e.g. –1) becomes + 0xFFFF FFFF (4 294 967 295). + +3. The old code tried to clamp the span only when the (optional) + feature flag 2578215227 was active *and* when + a5 >= imageWidth – currentX + The comparison is performed *before* the signed-to-unsigned cast + and therefore fails for negative numbers. As a result v7 keeps the + enormous value. + +4. v7 is passed to EpAlphaBlender::Blend as the *pixel count* to copy + from the source buffer (*this + 177) into the destination buffer + (*this + 171). Blend iterates width * bytesPerPixel bytes and thus + overruns the heap allocation backing the bitmap row. + +5. Because all pointers involved point to fully attacker-controlled + image data, the overflow can corrupt adjacent heap structures and + ultimately lead to arbitrary code execution in the calling + process. + +6. The auxiliary routine DpRegion::ValidateAndSet was also updated. + Although not the primary crash site, the original implementation + mixed signed/unsigned variables when building a dynamic array of + spans (PathBound). Crafting rectangles that triggered the + overlooked negative-value path (label 16/23) could leave the + builder in an inconsistent state and later cause similar heap + corruption during Set(). The patch rewrote the loop and removed + the flag that allowed this path to be taken. + +Key structure members affected + * this+0x08 (DWORD currentX) + * this+0x0C (DWORD currentY) + * this+0x510 (QWORD rowBufferPointer, index 171) + * this+0x528 (QWORD bitmapBase, 168) + * this+0x0144 (DWORD bitmapWidth, 328) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// EpScanBitmap::NextBuffer (before) +if (a5 && rowBuf) { + if (FeatureEnabled) { + if (a5 + currentX > bitmapWidth) + RtlLogUnexpectedCodepath(...); + if (a5 >= bitmapWidth - currentX) + v7 = bitmapWidth - currentX; // clamp + } + EpAlphaBlender::Blend(..., v7, ...); // v7 can be 0xFFFFFFFF +} + +// NextBufferWithBounds (before) +void *NextBufferWithBounds(..., unsigned int a5, ...) +{ + ... + if ((signed int)(currentX + a5) > maxX) + maxX = currentX + a5; // a5 wrapped from negative +} + +// After patch +void *NextBufferWithBounds(..., int a5, ...) +{ + ... + if (currentX + a5 > maxX) + maxX = currentX + a5; // signed addition +} + +if (FeatureEnabled && a5 >= bitmapWidth - currentX) + v7 = bitmapWidth - currentX; // no dangerous addition +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Malicious image -> GDI+ decoder -> EpScanBitmap::NextBufferWithBounds + (negative width) -> EpScanBitmap::NextBuffer (width cast to huge + unsigned) -> EpAlphaBlender::Blend (memcpy-style loop) -> heap buffer + overflow. + +Attack Vector +-------------------------------------------------------------------- +A crafted image, metafile or remote canvas operation that causes GDI+ +layout code to request a scanline with a negative span length. The +attack can be delivered via any channel that causes the target +application to load or manipulate the image (web content, email, or +network share). + +Patch Description +-------------------------------------------------------------------- +1. Changed NextBufferWithBounds parameter type from unsigned int to int + and removed the signed-cast shortcut in max-bound calculation. +2. In NextBuffer, removed the unsafe addition (a5 + currentX) and only + clamps when the span itself exceeds the remaining row width. +3. Re-ordered variables and reduced the number of signed/unsigned + casts. +4. Rewrote DpRegion::ValidateAndSet loop to prevent an illegal merge + path and to ensure DynamicArray::Grow failure cleanly aborts. + +Security Impact +-------------------------------------------------------------------- +An attacker can overflow a heap buffer inside GDI+, leading to memory +corruption and potential remote code execution in the context of the +calling process. Any application that processes untrusted images with +GDI+ is affected (Office, browsers, preview pane, etc.). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch eliminates the unsigned wrap by enforcing signed arithmetic +for the span length and removes the dangerous addition that could +overflow. It also improves span clamping in NextBuffer and tightens +rectangle validation in DpRegion. No remaining obvious path to +produce an oversized copy was found, suggesting the fix is effective. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62208_sppsvc.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62208_sppsvc.exe.txt new file mode 100644 index 0000000..05ccb0d --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62208_sppsvc.exe.txt @@ -0,0 +1,120 @@ +{'kb': 'KB5066835', 'file': 'sppsvc.exe', 'patch_store_uid': 'e036112f-b556-4011-858d-784aad5eed8e', 'change_count': 385, 'cve': 'CVE-2025-62208', 'confidence': 0.36, 'date': 1763409669.3225417} +-------------------------------------------------------------------- +CVE-2025-62208 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows License Manager Service (sppsvc.exe) – internal routines +sub_140103484 and sub_140102274, renamed after the update to +sub_140061C60 and sub_140061C10 respectively. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-532: Insertion of Sensitive Information into Log File + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch both routines implemented their own logic for +collecting and persisting license state flags. + +1. sub_140103484 (addr 0x140103484) + • Queries the Software Protection Platform (SPP) engine through an + indirect call located in qword_14046B468 / qword_14046B460. + • Derives several bit-fields (0xC00, 0x40, 0x80, etc.) that encode + activation status, grace period, tamper flags, etc. + • Calls sub_14010EBD4(&unk_14046CA30, …) – a runtime policy check + that can be bypassed when the previous evaluation sets the low + byte of the second argument to 0. + • When the policy permits, it fetches a 32-bit global variable + dword_14046C9D0 via sub_140102274 and stores the value in v15. + • Finally it logs this information with + sub_1400A5B88(&unk_14046C9D8, 45036776, (v15>>10)&1, + (v15>>11)&1, &v17, 1, 0); + The buffer v17 contains the caller-supplied input a1 combined with + WORD2(v17)=3, effectively writing raw licence state directly to + the SPP event channel. + +2. sub_140102274 (addr 0x140102274) + • Reads the same global (dword_14046C9D0) and, if certain flag bits + are not set, updates it by means of atomic + _InterlockedCompareExchange operations. + • Uses the same indirect function table (qword_14046B468 / 460) to + query policy, then decides whether to set or clear bits 0x40, 0x80 + and 0x400. + • When the global value transitions from "no flags" to a non-zero + value it invokes sub_140089A28 which triggers additional event log + entries. + +Because no caller authentication or data scrubbing is performed before +sub_1400A5B88 is executed, any local user able to reach the RPC or COM +endpoint that maps to these routines can force the service to emit its +current licence state into the Windows event log. The event log is +readable by BUILTIN\Users, so the information becomes available to +non-privileged accounts. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// --- extracted from pre-patch sub_140103484 --- +if ((unsigned __int8)sub_14010EBD4(&unk_14046CA30, v9)) { + v15 = dword_14046C9D0; + if ((dword_14046C9D0 & 4) == 0) { + v18 = *(_QWORD *)sub_140102274(&dword_14046C9D0, &v19); + v15 = v18; + } + LODWORD(v17) = 0; // user-controlled low dword + WORD2(v17) = 3; // constant 3 + sub_1400A5B88(&unk_14046C9D8, 45036776, + (v15 >> 10) & 1, + (v15 >> 11) & 1, + &v17, 1, 0); // ==> event log write +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker invokes the public SPP RPC/COM method that lands in + sub_140103484. +2. Function collects licence state bits and performs weak policy test + (sub_14010EBD4). +3. sub_140102274 is optionally invoked to refresh the global status + variable. +4. sub_1400A5B88 writes the computed licence information to the SPP + event channel (Event ID 45036776). +5. Attacker reads the Windows event log to obtain the data. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Any account that can call the License +Manager RPC/COM interface (normally available to regular users) can +trigger the vulnerable code path without additional privileges. + +Patch Description +-------------------------------------------------------------------- +Both vulnerable routines were entirely replaced with thin wrappers that +delegate the request to virtual methods of an internal object: + + v2 = *(QWORD *)(a1 + 0x10); + v4 = *(func **)(*v2 + 0x50); // 0x50 / 0x20 depending on function + _guard_check_icall_fptr(v4); + return v4(v2, a2); + +All previous logic – including bit manipulation, global variable access +and direct call to sub_1400A5B88 – has been removed. The delegated +methods can apply proper filtering or decide not to log at all. + +Security Impact +-------------------------------------------------------------------- +Pre-patch, non-privileged users could disclose licence and activation +state information that Microsoft considers sensitive (e.g. tamper +flags, product key hash bits, activation counters). This constitutes a +local information disclosure. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch eliminates the entire code sequence responsible for emitting +log entries; therefore the direct path that leaked information is gone. +Without the vulnerable subroutine the event 45036776 can no longer be +forced by untrusted input. Unless equivalent logging exists in the new +object method (not visible in the diff) the issue is fully mitigated. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62209_activationmanager.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62209_activationmanager.dll.txt new file mode 100644 index 0000000..39d99e8 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62209_activationmanager.dll.txt @@ -0,0 +1,111 @@ +{'file': 'activationmanager.dll', 'patch_store_uid': 'cc32b694-3173-4daa-afe6-6d44c45f18ba', 'kb': 'KB5066835', 'cve': 'CVE-2025-62209', 'confidence': 0.19, 'date': 1763409646.3919623, 'change_count': 5} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows License Manager / activationmanager.dll +Function: wil::details::FeatureImpl<...>::ReportUsage() +Module version prior to February 2025 security patch + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure – CWE-532: Insertion of Sensitive Information +into Log File + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper wil::details::FeatureImpl<>::ReportUsage() is responsible +for forwarding feature-usage telemetry to the Windows Instrumentation +library (WIL). A six-parameter call is performed to +wil::details::ReportUsageToService(). Parameter #6 represents the +"ReportingKind" enumeration which determines where usage data is +recorded (service-only, local log file, or both). + +Before the patch the second user-supplied argument (a2) was defined as +an unsigned 8-bit boolean and copied unvalidated into parameter #6. If +callers passed 0 (or any unexpected value) the routine selected the +"local log" path, causing the full usage payload – including licensing +state bits extracted from GetCachedFeatureEnabledState() – to be +written to an on-disk log file that is readable by any authenticated +local user. Because the data contains feature enablement flags and +other internal licensing metadata, the log constitutes sensitive +information. + +The vulnerable logic therefore allowed any in-process caller that could +invoke ReportUsage() to force insertion of licensing details into a +persistent, world-readable log, violating confidentiality. + +Structures / fields involved: + * Parameter #6 (wil::ReportingKind) of ReportUsageToService() + * Licensing state bits: (value >> 10)&1 and (value >> 11)&1 + * Temporary flag container v9 / v10 controlling WIL header flags + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +int v6 = a2; // caller-controlled +... +return wil::details::ReportUsageToService( + a1 + 2, + 54237977i64, + ((unsigned int)v4 >> 10) & 1, + ((unsigned int)v4 >> 11) & 1, + &v9, + v6, // unknowingly chooses log target + 0); + +// after +v9 = 3; // strict flags +... +return wil::details::ReportUsageToService( + a1 + 2, + 54237977i64, + (v4 >> 10) & 1, + (v4 >> 11) & 1, + &v8, + 1, // service-only, no local log + 0); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode code -> License Manager API -> ReportUsage(bool kind) + -> copies caller-controlled "kind" into ReportingKind + -> ReportUsageToService() with value 0 + -> WIL outputs sensitive licensing payload to local log file + +Attack Vector +-------------------------------------------------------------------- +A locally authenticated attacker that can load activationmanager.dll +and execute the public Feature ReportUsage() path (e.g., via any +application scriptable interface that wraps it) supplies 0 as the +second argument, forcing sensitive data into an easily accessible log +under %ProgramData%\Microsoft\Windows\WIL\logs. + +Patch Description +-------------------------------------------------------------------- +1. Formalised parameter types: second argument changed from unsigned + byte to 64-bit integer matching wil::ReportingKind enum. +2. The function now hard-codes ReportingKind to 1 (service-only), + preventing callers from selecting local logging. +3. Header flag v9 initialised to 3 instead of uninitialised 0/2 to + comply with updated WIL contract. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any local user could coerce Windows License Manager +into persisting internal licensing state, potentially revealing feature +activation status and diagnostic identifiers that aid further +privilege-escalation or license circumvention efforts. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes caller influence over the ReportingKind field; usage +is now always sent directly to the telemetry service with no local log +side-effects. Because the constant is set inside the function and the +parameter type no longer matches the external prototype, the original +misuse path is fully closed. No obvious residual path remains. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62213_afd.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62213_afd.sys.txt new file mode 100644 index 0000000..478cd81 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62213_afd.sys.txt @@ -0,0 +1,113 @@ +{'cve': 'CVE-2025-62213', 'patch_store_uid': 'dd9c6721-2756-4ad6-92a0-850c704dd674', 'confidence': 0.34, 'kb': 'KB5068861', 'date': 1763412304.0701337, 'file': 'afd.sys', 'change_count': 5} +-------------------------------------------------------------------- +CVE-2025-62213 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel driver afd.sys (Ancillary Function Driver for WinSock) +versions prior to the April-2025 security update. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416 – Use-After-Free / Dangling-pointer access leading to local +privilege escalation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AFD endpoint objects can be *un-bound* (freed) by a user process via +IOCTLs or closes. Several code paths (SIO_SOCKET_TRANSFER_BEGIN / END, +AfdBind, AfdConnect and AfdGetInformation) accessed internal endpoint +fields after assuming the object would remain valid: + + • v5/v4 – pointer to the AFD_ENDPOINT structure + • v9/v10 – cached DEVICE_OBJECT + • v8 – cached connection pointer + +Before the patch these routines: + 1. copied the DEVICE_OBJECT and FILE_OBJECT from the endpoint; + 2. queued an IRP to the transport with IofCallDriver(); + 3. returned to user mode *without* holding any additional reference + on the endpoint or connection. + +If the caller (or another thread with a duplicated handle) closed or +unbind the socket before the IRP finished, AfdDereferenceEndpoint() +freed the object. The completion routine, or later instructions in the +same function, then dereferenced the stale pointers, producing a +use-after-free in kernel context. + +Because the freed memory is controlled by the attacker (it resides in +NonPagedPool) the dangling write/read can be exploited to overwrite +adjacent kernel objects and run arbitrary code with SYSTEM privileges. +No special capabilities are needed beyond a handle to a local socket. + +The bug is reachable for: + • Bluetooth RFCOMM endpoints ("\Device\BTHMS_RFCOMM") + • regular TCP/UDP endpoints that are not yet bound + • endpoints whose type is neither AFD_ENDPOINT_TYPE_V4(1) nor + V6(6) (the extra checks added by the patch). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +v8 = *(DEVICE_OBJECT **)(v4 + 40); +v9 = *(FILE_OBJECT **)(v4 + 24); +_IofCallDriver(v8, Irp); // endpoint can be freed right here +... + +// After +if (!AfdPreventUnbind(v5)) // grab a "no-unbind" reference + return STATUS_CONNECTION_ABORTED; +v3 = 1; // remember the lock +... +Status = IofCallDriver(v9, Irp); +... +if (v3) // release only after use + AfdReallowUnbind(v5); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens an un-bound socket (CreateFileA on "\\Device\\Afd"). +2. Issue IOCTL_AFD_SOCKET_TRANSFER_BEGIN / BEGIN_BIND / CONNECT. +3. Immediately close the handle or call IOCTL_AFD_UNBIND from another + thread. +4. Endpoint memory is freed. +5. Transport IRP completes and AfdTdiSocketTransferComplete (or similar) + touches the freed endpoint –> kernel UAF. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user-mode code. Requires only the ability to open +and close a Winsock socket; no administrator rights or special +privileges are needed. + +Patch Description +-------------------------------------------------------------------- +Microsoft inserted systematic lifetime-management around every code path +that accesses an un-bound endpoint: + • New helpers AfdPreventUnbind() / AfdReallowUnbind() are called to + raise a reference counter that blocks the unbind operation for the + duration of the sensitive region. + • Additional endpoint-type checks (family == 1/6, state flags, IRQL) + cause early FAIL_FAST if the object is already in a dangerous + state. + • In AfdGetInformation/AfdBind/AfdConnect extra parameter validation + prevents undersized sockaddr buffers and illegal combinations. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a local attacker could dereference freed kernel memory +from ring-0, allowing information disclosure or, with pool spraying, +arbitrary kernel code execution. Successful exploitation yields SYSTEM +privileges (Elevation-of-Privilege class). + +Fix Effectiveness +-------------------------------------------------------------------- +The added unbind-prevention reference guarantees that the endpoint +structure cannot be freed until after the IRP or helper routine has +finished, removing the dangling pointer window. Combined with stricter +parameter validation, the patch completely eliminates the observed +use-after-free condition. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62215_ntoskrnl.exe.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62215_ntoskrnl.exe.txt new file mode 100644 index 0000000..e87c8db --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62215_ntoskrnl.exe.txt @@ -0,0 +1,150 @@ +{'confidence': 0.08, 'date': 1763410768.588055, 'change_count': 86, 'cve': 'CVE-2025-62215', 'file': 'ntoskrnl.exe', 'patch_store_uid': 'ffde7bce-1b70-400a-83c8-6714efbb59c3', 'kb': 'KB5068861'} +-------------------------------------------------------------------- +CVE-2025-62215 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel – Security Reference Monitor (SRM) + • SepCopyTokenAccessInformation + • SepDuplicateToken + • SepTokenDeleteMethod +These routines build, duplicate and destroy the kernel TOKEN object. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition / Improper Synchronisation +CWE-415: Double Free (resulting from the race) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When the optional SID-sharing feature (SepTokenSidSharingEnabled) is +on, the TOKEN does not hold a private copy of the User/Groups SID +array. Instead it points to a shared, reference-counted buffer that is +kept in *Token+0x470 (old code) / *Token+0x468 (new code). + +1. SepDuplicateToken + • Before the patch the routine merely copied the pointer of the + shared buffer into the destination TOKEN (p_Body[19]) + but DID NOT increment the reference counter that lives eight + bytes before the buffer. + • The pointer was therefore shared by an arbitrary number of + TOKENs that all believed they owned the memory. + +2. SepTokenDeleteMethod + • On handle close the function called + SepDeleteTokenUserAndGroups() which unconditionally freed the + buffer pointed to by *Token+0x470 and set the field to NULL. + • No interlocked decrement was performed, no lock was taken and + the other TOKEN instances still kept their (now dangling) + pointer. + +3. Concurrent destruction of two such TOKENs therefore leads to the + classic pattern: + T1: free(shared_buffer) + T2: free(shared_buffer) <-- double free / UAF + +Because the buffer is allocated from paged pool an attacker can race +thread T1 and immediately reclaim the freed slot with controlled data. +Later kernel reads/writes (e.g. SID membership tests or hash rebuilds) +are then performed on attacker supplied memory, allowing elevation of +privileges. + +Additional unsigned-to-signed mistakes existed in +SepCopyTokenAccessInformation – length parameters were declared +‘int’, so negative values underflowed the size subtraction +( v22 - a6 etc.) allowing out-of-bounds copies. The patch converts +all length arguments to unsigned and adds explicit range checks; these +bugs became exploitable after the UAF because the attacker could craft +malformed length fields inside the reclaimed buffer. + +Structures / fields affected (Windows 64-bit): + TOKEN.UserAndGroups at +0x098 + TOKEN.SharedSidHeader at +0x470 (refcnt @-8) + TOKEN.VariablePartSize at +0x084 / +0x088 + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// SepDuplicateToken – BEFORE +p_Body[19].UseThisFieldToCopy = v42 + *((_QWORD *)a1 + 19); // copy ptr +... +if (_InterlockedIncrement64((volatile signed __int64 *) + (*(( _QWORD *)a1 + 141) + 8)) <= 1) + __fastfail(0xE); // increments *different* counter +``` +```c +// SepTokenDeleteMethod – BEFORE +if (SepTokenSidSharingEnabled) + SepDeleteTokenUserAndGroups(a1); // frees shared buffer blindly +``` +```c +// SepDuplicateToken – AFTER (excerpt) +if (SepTokenSidSharingEnabled) { + v58 = 0; + // take shared buffer pointer + MEMORY[0x98] = *(_QWORD *)(a1 + 152) - a1; + // *increment correct reference counter* + if (_InterlockedIncrement64((volatile signed __int64 *) + (MEMORY[0x98] - 8)) <= 1) __fastfail(0xE); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker enables SID-sharing (system-wide GFlags or policy). +2. Thread A continuously duplicates its process token → many TOKENs + share the same User/Group buffer. +3. Threads A and B close two TOKEN handles at the same time. +4. The first close frees the shared buffer; the second close frees the + same address again. +5. Attacker reallocates the memory between the two frees, gains kernel + read/write via stale pointer, and overwrites privilege bits/LUIDs. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker who can call DuplicateTokenEx and close +handles in parallel (e.g. from two threads in the same process). +No additional privileges are required because a normal user can always +open and duplicate its own primary token. + + +Patch Description +-------------------------------------------------------------------- +1. All size/offset parameters in SepCopyTokenAccessInformation changed + from signed ‘int’ to unsigned to eliminate underflow. +2. Introduction of strict per-copy bounds checks and loop counters when + copying SID_AND_ATTRIBUTES arrays. +3. SepDuplicateToken now: + • Takes an interlocked reference on every shared SID buffer + (InterlockedIncrement64(ptr-8)). + • Adds matching InterlockedDecrement in the error paths. +4. SepTokenDeleteMethod now: + • Decrements the shared buffer refcount atomically; frees only when + the count reaches zero. + • Zeros the Token fields after free to prevent a second free. + • Uses new helpers (SepDereferenceLowBoxNumberEntry, etc.) guarded + by proper locks. +5. Numerous defensive NULL assignments and interlocked checks added. + + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could trigger a race that leads to a +use-after-free / double-free of a paged-pool object inside the kernel. +Exploiting the dangling pointer allows arbitrary kernel memory writes +and therefore full SYSTEM privileges. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the unsafe signed arithmetic, introduces explicit +length validation, and converts the shared SID buffer to a properly +reference-counted object with atomic increment/decrement on every path +(duplication, deletion, and failure unwind). Double free is therefore +no longer reachable and the original race window is closed. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62217_afd.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62217_afd.sys.txt new file mode 100644 index 0000000..fb6e963 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62217_afd.sys.txt @@ -0,0 +1,139 @@ +{'cve': 'CVE-2025-62217', 'file': 'afd.sys', 'patch_store_uid': 'dd9c6721-2756-4ad6-92a0-850c704dd674', 'change_count': 5, 'kb': 'KB5068861', 'date': 1763409913.4688723, 'confidence': 0.24} +-------------------------------------------------------------------- +CVE-2025-62217 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel driver +File: afd.sys (Ancillary Function Driver for WinSock) +Affected routines: + * AfdSocketTransferBegin / AfdSocketTransferEnd + * AfdGetInformation + * AfdBind + * AfdConnect + + +Vulnerability Class +-------------------------------------------------------------------- +Concurrent execution using shared resource with improper +synchronization (race condition) leading to use-after-free / dangling +pointer dereference inside kernel-mode socket endpoint objects. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AFD endpoint structures can be *unbound* (logically detached and +released) at any time by user code that closes a handle or issues +IOCTL_AFD_UNBIND. Prior to the patch a large family of handler +functions (Bind, Connect, SocketTransfer*, GetInformation) dereferenced +the endpoint, its connection, and its underlying TDI device pointers +without first guaranteeing that the endpoint cannot be un-bound in +parallel. + +Execution flow: +1. Thread-A starts one of the vulnerable IRP paths and obtains a raw + pointer to the endpoint (e.g. `v4 = *(a2+48)->FileObject->FsContext`) + and later to the connection object. +2. Before the IRP finishes, Thread-B closes the same socket or calls + the unbind IOCTL. This drops the last reference and frees the + endpoint / connection memory. +3. Thread-A resumes and continues to access fields inside the now + freed structures (`v4+248`, `v7+48`, `v5+264`, etc.). This is a + classic use-after-free that results in arbitrary kernel read/write + and ultimately privilege escalation. + +The race could be triggered reliably because none of the affected +functions raised the in-flight reference count that normally prevents +unbind (`AfdEndpoint->UnbindPreventCount`). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (AfdSocketTransferEnd): +```c +v4 = *(FileObject->FsContext); +... +if (!v8) { // v8 = DeviceObject extracted from endpoint + Status = STATUS_INVALID_PARAMETER; + goto complete; +} +... +*(_WORD *)&v11[-1].MajorFunction = 21007; // continues to use v4 +``` +After patch (excerpt): +```c +if (*(_WORD*)v5!=0xAFD1 && *(_WORD*)v5!=6909 && + ((*(_BYTE*)(v5+5)&0x10)==0 || (*(_BYTE*)(v5+6)&1)==0) && + (*(_DWORD*)(v5+8)&1)==0 && *(_BYTE*)(v5+2)!=4) { + if (!(UINT8)AfdPreventUnbind(v5)) { + Status = STATUS_PORT_DISCONNECTED; // bail out + goto cleanup; + } + v3 = 1; // remember to re-allow later +} +... +cleanup: +if (FeatureEnabled && v3) + AfdReallowUnbind(v5); // release the hold +``` +The same two-step *PreventUnbind / ReallowUnbind* sequence has been +added to all other touched routines. + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process opens an AFD socket handle. +2. Thread-A issues IOCTL_AFD_BIND / CONNECT / TRANSFER / QUERY, placing + the vulnerable routine on the stack. +3. Just after the routine reads the endpoint pointer, Thread-B closes the + handle or issues IOCTL_AFD_UNBIND causing the endpoint to be freed. +4. Thread-A continues execution and touches freed memory, corrupting + pool or executing attacker-controlled data. + + +Attack Vector +-------------------------------------------------------------------- +Local, low-privilege user-mode code. No special rights beyond the +ability to create and manipulate Winsock sockets are required. The +race can be driven entirely from user space with two threads and normal +AFD IOCTLs. + + +Patch Description +-------------------------------------------------------------------- +1. Introduces helper pair `AfdPreventUnbind()` / `AfdReallowUnbind()` + that atomically increments / decrements the endpoint field that gates + unbind operations. +2. Each vulnerable routine now: + a. Detects whether the endpoint is susceptible to unbind (various + fast-path exclusions remain for raw or already protected sockets). + b. Calls `AfdPreventUnbind()` before dereferencing the endpoint. + c. Stores a flag so that `AfdReallowUnbind()` is executed on every + exit path (success, failure or early return). +3. Adds extra parameter and state validation to bail out early on + malformed input. +4. Minor refactoring: renamed local variables, widened IRP status plane, + and added feature-flag wrappers so the mitigation can be enabled / + disabled via GFlags. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a local attacker could win the race to unbind and +convert the freed endpoint / connection memory into a controlled fake +object. Subsequent kernel dereferences allow arbitrary kernel pointer +reads/writes, resulting in elevation to SYSTEM. The bug therefore +constitutes a local Elevation of Privilege (EoP) vulnerability in the +Windows kernel. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added *PreventUnbind* reference guarantees that the endpoint remains +allocated while the routine is operating, closing the race window. The +complementary *ReallowUnbind* in every exit path prevents leaks. +Additional sanity checks further reduce malformed input surfaces. No +remaining unprotected dereference paths were observed in the patched +code, so the fix appears comprehensive. + diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62217_tcpip.sys.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62217_tcpip.sys.txt new file mode 100644 index 0000000..58e2f71 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62217_tcpip.sys.txt @@ -0,0 +1,122 @@ +{'change_count': 43, 'patch_store_uid': '600ccb23-b28b-4f6d-b005-d53a5d35cf13', 'cve': 'CVE-2025-62217', 'file': 'tcpip.sys', 'kb': 'KB5068861', 'confidence': 0.1, 'date': 1763410055.1874998} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (AFD.SYS) – ALE +(Access-control List Enforcement) cache handling routine +AleEdgeIFsFlushCache(). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Concurrent execution using shared resource with improper +synchronisation (race condition). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The endpoint structure that represents a user socket contains a +small cache of "edge" interface (IF) entries: + +0x130 : spin-lock (embedded) + +0x130+0xB0 (0x304) : list head of cached IF records + +0x140+0x04 (0x324) : number of cached IF records + +0x150+0x08 (0x328) : state flags (bit0 = cache valid) + +0x150+0x10 (0x336) : cached interface index. + +AleEdgeIFsFlushCache() is invoked whenever the interface index +attached to the socket changes. The routine acquires the +endpoint spin-lock, compares the caller-supplied index (a2) with +the one stored at offset +0x336 and, when different, clears the +old cache and drops the list pointer at +0x304 for later cleanup +(by calling AleEdgeIFsCleanup()). + +In the vulnerable build the routine updates the structure members +in the following (non-atomic) order under the lock: + 1. Writes the new interface index to +0x336 + 2. NULLs the list head at +0x304 + 3. Clears the status bytes at +0x320 / +0x321 + 4. Finally zeroes the element counter at +0x324. + +Because the counter (+0x324) is written *after* the list head is +reset, another thread that enters the code path immediately after +step 2 will observe a NULL list pointer while the counter is still +non-zero. That thread will conclude that a cache already exists +(size > 0) but that the list head is unexpectedly NULL, an +invariant violation. Subsequent code that iterates the list +through the stale counter will therefore dereference a NULL / freed +address, leading to pool corruption or an attacker-controlled +use-after-free. This enables local privilege escalation. + +Patch 27114 changes the write-order: the counter at +0x324 is now +reset *first*, before any other field is modified, guaranteeing +that a reader can never observe the inconsistent (counter!=0 && +list==NULL) state. The function has also been converted from void +to __int64 and now returns the value produced by AleEdgeIFsCleanup +so that the caller can propagate any error code, but that change is +cosmetic with regard to the race. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable order (before) +if (a2 != *(DWORD *)(a1 + 336)) { + *(DWORD *)(a1 + 336) = a2; // 1 new index + *(QWORD *)(a1 + 304) = 0; // 2 NULL list head + *(BYTE *)(a1 + 320) = 0; + *(BYTE *)(a1 + 321) = 0; + *(DWORD *)(a1 + 324) = 0; // 4 counter reset (too late) +} + +// fixed order (after) +if (a2 != *(DWORD *)(a1 + 336)) { + *(DWORD *)(a1 + 324) = 0; // 1 counter first + *(DWORD *)(a1 + 336) = a2; + *(QWORD *)(a1 + 304) = 0; + *(BYTE *)(a1 + 320) = 0; + *(BYTE *)(a1 + 321) = 0; + v3 = *(QWORD *)(a1 + 312); + *(QWORD *)(a1 + 312) = 0; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread A (local attacker) issues socket operation that causes an + interface index change and enters AleEdgeIFsFlushCache(). +2. Thread A takes the endpoint spin-lock, performs step 1 & 2 of + the vulnerable sequence, then gets pre-empted. +3. Thread B (victim or attacker-controlled) now enters + AleEdgeIFsFlushCache() or any other routine that looks at the + cache. It sees counter>0 and list==NULL, dereferences invalid + memory and corrupts kernel pool. +4. The corrupted pool allows the attacker to escalate privileges. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running crafted Winsock operations on +multiple threads / cores to race the ALE endpoint cache flush. +No elevated privileges or special capabilities required. + +Patch Description +-------------------------------------------------------------------- +Microsoft moved the write to the cache counter (+0x324) ahead of +all other field updates, restoring atomicity of the cache state. +The return type was also switched to __int64 so that the status +returned by AleEdgeIFsCleanup() can be propagated, but that has no +impact on exploitation. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could reliably hit a +use-after-free / null-dereference window inside the network stack, +leading to elevation of privilege (ring-0 execution) or, at a +minimum, a denial of service (bugcheck). + +Fix Effectiveness +-------------------------------------------------------------------- +The reorder ensures that at no time can another thread observe an +invalid combination of list pointer and element count. As both +values are written while holding the same spin-lock, the race +condition is completely removed. No residual paths that use the +old ordering remain, so the fix is judged effective. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62218_provcore.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62218_provcore.dll.txt new file mode 100644 index 0000000..1fd05a4 --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62218_provcore.dll.txt @@ -0,0 +1,105 @@ +{'change_count': 15, 'file': 'provcore.dll', 'cve': 'CVE-2025-62218', 'patch_store_uid': 'e0153e30-b6d6-4f95-a5e6-5c7ee980e3fc', 'kb': 'KB5068861', 'date': 1763409893.2063422, 'confidence': 0.17} +-------------------------------------------------------------------- +CVE-2025-62218 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +provcore.dll – Microsoft Wireless Provisioning System (COM class +ProvisioningEngine) and related ATL wrapper objects. + +Vulnerability Class +-------------------------------------------------------------------- +Race condition / improper synchronisation (CWE-362). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The ProvisioningEngine class maintains a deque (member +m_msgTimeWindow) that records timestamps used by +IsMessageThrottlingRequired(). Prior to the patch this container was +accessed from different threads by +ProvisioningEngine::ProcessNotification() without any mutual exclusion +and its dedicated critical section was never created. + +Constructor chain before the patch + ATL::CComObject<ProvisioningEngine>::CComObject<>() + • no call to InitializeCriticalSection on m_msgTimeWindowLock + +Process flow before the patch + ProcessNotification() → IsMessageThrottlingRequired(*deque) + → direct container access (read/write) while other threads can enter +the same code path concurrently. + +Because the deque can reallocate its internal buffer, concurrent +push/pop operations lead to use-after-free or memory corruption in the +address space of the privileged COM service. An attacker able to send +parallel provisioning notifications can trigger the corrupt state and +execute arbitrary code in the service context, thereby elevating local +privileges. + +Structures/fields affected + • ProvisioningEngine::m_msgTimeWindow (std::unique_ptr<deque<u64>>) + • ProvisioningEngine::m_msgTimeWindowLock (RTL_CRITICAL_SECTION) + • ATL wrapper objects that embed a ProvisioningEngine instance + (CComObject / CComAggObject). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – no lock, CS never initialised +if (IsMessageThrottlingRequired( + *(std::deque<unsigned __int64> **)&this->m_critsec.m_bInitialized)) +{ + CurrentThread = GetCurrentThread(); + SetThreadPriority(CurrentThread, 0x10000); +} +``` +```c +// AFTER – critical section created and used +EnterCriticalSection(&this->m_msgTimeWindowLock.m_cs); +wil::unique_any lock(&this->m_msgTimeWindowLock.m_cs); +bool throttle = IsMessageThrottlingRequired( + *(std::deque<unsigned __int64> **)&this->m_critsec.m_bInitialized); +// lock goes out of scope -> LeaveCriticalSection() +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client thread(s) → IProvisioningEngine::ProcessNotification() + → unsynchronised access to m_msgTimeWindow deque → concurrent thread +enters same path → container reallocation/use-after-free → memory +corruption → attacker-controlled code execution in service process. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker launches multiple threads (or processes) +that simultaneously invoke the COM interface +IProvisioningEngine::ProcessNotification on the Wireless Provisioning +System service, racing the shared deque manipulation. + +Patch Description +-------------------------------------------------------------------- +1. Added member m_msgTimeWindowLock (RTL_CRITICAL_SECTION). +2. ProvisioningEngine::ProvisioningEngine now calls + InitializeCriticalSectionEx(&m_msgTimeWindowLock). +3. Destructor deletes the critical section. +4. ProcessNotification now wraps the throttle-check inside + Enter/LeaveCriticalSection using wil::unique_any_t helper. +5. ATL wrapper constructors/destructors updated to invoke the new + constructor/destructor so the critical section is correctly created + and destroyed for every object instance. +6. Allocation sizes in CreateInstance adjusted for the larger object. + +Security Impact +-------------------------------------------------------------------- +Before the patch, concurrent unsynchronised access allowed memory +corruption in a privileged service, resulting in a local Elevation of +Privilege (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +All entry points that access m_msgTimeWindow are now protected by a +properly initialised critical section, eliminating the race window. +No remaining unsynchronised accesses were observed in the diff, +indicating the fix is sufficient as long as future code paths respect +the same locking discipline. diff --git a/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62219_provcore.dll.txt b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62219_provcore.dll.txt new file mode 100644 index 0000000..3c61c6f --- /dev/null +++ b/reports/2025-nov-12390-windows_11_24H2_x64/CVE-2025-62219_provcore.dll.txt @@ -0,0 +1,128 @@ +{'change_count': 15, 'file': 'provcore.dll', 'patch_store_uid': 'e0153e30-b6d6-4f95-a5e6-5c7ee980e3fc', 'kb': 'KB5068861', 'date': 1763412749.1534865, 'confidence': 0.25, 'cve': 'CVE-2025-62219'} +-------------------------------------------------------------------- +CVE-2025-62219 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Wireless Provisioning System – provcore.dll +(ProvisioningEngine COM class together with helper Feature* code) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-415: Double Free +CWE-362: Race Condition (unsynchronised access to shared data) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ProvisioningEngine keeps a per-object unique_ptr<std::deque<ulong64>> +called m_msgTimeWindow. The deque is used from +ProcessNotification() to decide whether an incoming provisioning +message must be throttled (IsMessageThrottlingRequired()). + +In the original code path the deque was accessed without any form of +synchronisation: + • every call to ProcessNotification() looked at / modified the same + deque instance; + • the same object could be reached from several STA / MTA threads + through the IProvisioningEngine COM interface that is exposed by +a system service. + +Because no critical-section protected the container, two threads could +concurrently pop / push elements causing the STL implementation to +free and re-allocate internal blocks while another thread still held +obsolete pointers. When the second thread later executed an oper­ +a­tion that triggered a new free() the allocator was presented with an +already freed block, corrupting the heap – a classic double free. + +Additionally, object lifetime was mishandled: + • ATL::CComAggObject and CComObject wrappers invoked the + ExtensionPreloadData dtor instead of the real + ProvisioningEngine dtor. The real resources (deque + critical + section) were therefore destroyed again when the inner object was + finally deleted, producing a second double free avenue. + +Exploiting either of the above allows an attacker running in the same +session to obtain heap-layout control and execute arbitrary code in +the service’s security context (SYSTEM). + +Structures / members involved + ProvisioningEngine + +0x18 m_critsec (CComSafeDeleteCriticalSection) + +0x30 m_msgTimeWindowLock (added by patch) + +0x58 m_msgTimeWindow (std::unique_ptr< deque<ulong64> >) + +The race happens while ProcessNotification executes: + IsMessageThrottlingRequired(*(deque**)&this->m_critsec.m_bInitialized) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (no lock, two threads can enter simultaneously) +```c +v9 = 0; +// … parameter validation … +if (IsMessageThrottlingRequired(*(std::deque<unsigned __int64>**)&this->m_critsec.m_bInitialized)) +{ + CurrentThread = GetCurrentThread(); + if (SetThreadPriority(CurrentThread, 0x10000)) + v10 = 1; +} +``` +After (explicit critical-section serialises access) +```c +EnterCriticalSection(&this->m_msgTimeWindowLock.m_cs); +lock.m_ptr = (_RTL_CRITICAL_SECTION *)&this->m_msgTimeWindowLock.m_cs; +bool throttle = IsMessageThrottlingRequired( + *(std::deque<unsigned __int64> **)&this->m_critsec.m_bInitialized); +// unique_any_t automatically leaves the CS on scope exit +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege client obtains IProvisioningEngine instance + (CreateInstance → COM out-of-proc service running as SYSTEM). +2. Client creates two or more threads. +3. All threads call ProcessNotification() with crafted but valid + GUID / notification arguments. +4. Unsynchronised access to m_msgTimeWindow causes concurrent + deque mutations. +5. Heap allocator frees the same chunk twice, leading to memory + corruption and potential code execution in SYSTEM context. + +Attack Vector +-------------------------------------------------------------------- +Local authenticated attacker. By invoking the public COM interface +(IProvisioningEngine) from any sandbox / desktop process and issuing +parallel ProcessNotification calls the race can be won reliably. + +Patch Description +-------------------------------------------------------------------- +1. Added new member m_msgTimeWindowLock (RTL_CRITICAL_SECTION). +2. ProvisioningEngine constructor now calls + InitializeCriticalSectionEx() to create the lock. +3. Destructor now: + • leaves and deletes the lock via DeleteCriticalSection; + • explicitly destroys m_msgTimeWindow unique_ptr and existing + CComSafeDeleteCriticalSection. +4. ProcessNotification now unconditionally serialises access to the + deque with EnterCriticalSection/LeaveCriticalSection wrapped by + wil::unique_any_t. +5. All wrapper classes (CComObject/CComAggObject and their creators) + were updated to call the correct ProvisioningEngine ctor / dtor so + resources are released exactly once. + +Security Impact +-------------------------------------------------------------------- +An attacker could execute arbitrary code with SYSTEM privileges or +cause a service crash, resulting in an elevation of privilege (EoP). +The issue is memory-safety critical and bypasses standard user / +service isolation boundaries. + +Fix Effectiveness +-------------------------------------------------------------------- +The critical-section eliminates concurrent access to the shared deque +and therefore removes the race that led to the double free. Correct +constructor / destructor routing ensures resources are released once +only. No further double-free code paths are visible in the patched +binary; effectiveness is considered HIGH, although stress testing +under extreme concurrency is recommended. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2016-9535_windowscodecs.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2016-9535_windowscodecs.dll.txt new file mode 100644 index 0000000..0aace06 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2016-9535_windowscodecs.dll.txt @@ -0,0 +1,129 @@ +{'cve': 'CVE-2016-9535', 'kb': 'KB5066835', 'date': 1763403050.2118447, 'file': 'windowscodecs.dll', 'patch_store_uid': '770b2931-a4a8-483f-bd9d-fa1d9120b168', 'confidence': 0.2, 'change_count': 30} +-------------------------------------------------------------------- +CVE-2016-9535 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Imaging Component (WIC) – windowscodecs.dll. The vulnerable +code is the built-in copy of LibTIFF predictor routines +(PredictorEncode/Decode and helper functions such as horAcc*/horDiff*, +fpAcc/fpDiff, swabHorAcc16) and the tile initialisation helper +CLibTiffDecoderBase::CheckTiledTIFF. + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow arising from missing parameter validation +and integer-overflow in size/stride calculations (CWE-122, CWE-190). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. All predictor helpers receive a caller-supplied byte count (a3) that + is derived from image tile/row dimensions stored in the TIFF file. + The functions assumed that: + • a3 was non-negative, and + • a3 was an exact multiple of the per-pixel stride held in the + codec context (*((int*)(ctx+432)+1)). +2. When these assumptions were false the loops that walk the buffer + (e.g. horDiff8/horAcc8/horAcc16/horDiff16) executed a + "while(vRemaining>=stride)" pattern, updating the buffer by + *adding* or *subtracting* values that lay stride bytes back. +3. If a3 < 0 the loop condition was true after unsigned promotion and + wrote far beyond the start of the heap buffer. If a3 was positive + but not divisible by stride the final iteration(s) wrote past the + end of the allocation. +4. CLibTiffDecoderBase::CheckTiledTIFF contained similar unchecked + arithmetic when computing tile counts: + numTiles = (width+tileW-1) / tileW; // potential overflow + followed by an allocation sized using the wrapped value, allowing + later predictor helpers to operate on undersized memory. +5. In all cases the size/stride values originate from the attacker + controlled TIFF header, so a malformed image could reliably trigger + an out-of-bounds write in a process that merely opens or previews + it. + +Affected parameters / structures +- a3: byte count per row/tile passed to predictor helpers. +- ctx+432: structure holding stride and function table. +- this+824/828: tileW/tileH in CheckTiledTIFF. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (horAcc8): +```c +v5 = *(int *)(result + 4); // stride +if (a3 > (int)v5) { + if (!(a3 % (int)v5)) { + v6 = (unsigned int)(a3 - v5); + ... // unguarded loop + } +} +``` +After: +```c +v6 = *(int *)(*(_QWORD *)(a1+432)+4); +if (IsEnabledFeat2578 && a3 % (int)v6) + return 0; // reject mis-aligned length +... +return 1; // success flag +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Application loads crafted TIFF. +2. WIC calls CLibTiffDecoderBase::CheckTiledTIFF -> mis-sized tile + values are accepted (pre-patch) and small heap buffer allocated. +3. During decoding Predictor*Encode/Decode helpers are invoked with a3 + taken from the TIFF directory. +4. horDiff*/horAcc*/fpAcc/fpDiff loops run past allocation and corrupt + heap, leading to crash or controlled data overwrite. + + +Attack Vector +-------------------------------------------------------------------- +Opening, previewing, or indexing a malicious TIFF image (e.g. via File +Explorer thumbnail, Outlook attachment, Edge, or any third-party +program that relies on WIC) triggers the vulnerable code inside the +calling process. + + +Patch Description +-------------------------------------------------------------------- +• All helpers now treat the byte-count argument as unsigned and verify + the sign bit. +• Added feature-gated runtime checks that reject rows/tiles whose + length is not an exact multiple of stride or (bps*stride). + On failure TIFFErrorExt is invoked and the function returns 0. +• Loops were re-written to use early exit and 64-bit counters; several + helpers changed their return type to propagate success/failure. +• CheckTiledTIFF gained strict validation for + – stride alignment (multiple of 16), + – non-zero tile width/height, + – integer overflow in (w+h) arithmetic. + It now returns errors before allocating, and the allocation is sized + with size_t-safe arithmetic. +• Defensive code paths are enabled under the internal WIL feature id + 2578215227. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a crafted TIFF could cause a heap buffer overflow +leading to process crash or potential remote code execution in the +context of the calling application. The issue is tracked as +CVE-2016-9535. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added validation fully blocks the originally reported misaligned +or negative sizes and protects all arithmetic that previously +overflowed. Because the hardened paths are conditional on a Feature +flag, effectiveness relies on the flag being enabled in production +builds; if disabled the legacy vulnerable behaviour persists. +No residual overflow conditions were observed in the updated code. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-24052_ltmdm64.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-24052_ltmdm64.sys.txt new file mode 100644 index 0000000..f394b6e --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-24052_ltmdm64.sys.txt @@ -0,0 +1,140 @@ +{'change_count': 5, 'date': 1763407839.0170572, 'confidence': 0.23, 'kb': 'KB5066835', 'file': 'ltmdm64.sys', 'cve': 'CVE-2025-24052', 'patch_store_uid': '2ef9587d-d12e-4efa-97f8-9cdcd571719c'} +-------------------------------------------------------------------- +CVE-2025-24052 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +ltmdm64.sys (Agere/Lucent Technologies software-modem kernel driver, +serial-port emulation path) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-121: Stack-based Buffer Overflow (leading to arbitrary kernel code +execution / Elevation of Privilege) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine is SerialGetConfigDefaults(). During driver +initialisation DriverEntry() calls + + SerialGetConfigDefaults(&driverDefaults); + +The callee receives two parameters: + • Dst – pointer to a 0x78-byte structure that will hold driver + defaults (supplied by the caller, usually allocated on the stack). + • a2 – pointer to a UNICODE_STRING-like pair (Length in the first + WORD, Buffer pointer in the next QWORD). + +Root-cause sequence inside the function (before the patch): + 1. The function calculates + SIZE_T v4 = *(WORD *)a2 + 2; + and allocates a paged-pool buffer of that size. + 2. memmove_91() copies *(WORD*)a2 bytes from user-controlled + a2[1] into the freshly allocated buffer without any additional + validation. + 3. A fixed, compiler-generated RTL_QUERY_REGISTRY_TABLE array + (QueryTable[9]) lives entirely on the *stack* (0x1C0 bytes). + SerialGetConfigDefaults() memset()s the array and then writes + seven entries containing pointers into the attacker-controlled + paged-pool buffer. Because the function never sets a hard + terminator entry (Name==NULL), RtlQueryRegistryValues() continues + to walk the stack beyond the 9-element array, interpreting stack + data as additional entries. + 4. Any non-zero QWORD that sits behind QueryTable[8] will be treated + as an RTL_QUERY_REGISTRY_TABLE element, giving the attacker full + read/write access to adjacent stack memory. By crafting an +over-long registry path string the attacker can reliably overwrite + the saved return address (or the stored security cookie) that is + located after the array, leading to a classic stack overflow and + eventual kernel-mode code execution. + +Key data that can be influenced by a non-privileged user: + • The registry path passed via IoCreateDevice / AddDevice. + • Arbitrary registry values that will be parsed through the overrun + RTL_QUERY_REGISTRY_TABLE entries. + +Because the vulnerable code executes inside DriverEntry(), the bug is +reachable as soon as the binary is loaded (either automatically during +boot or manually through the service control manager). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// simplified extract from the original code +SIZE_T v4 = *(WORD *)a2 + 2; // length comes from user +PVOID Pool = ExAllocatePoolWithTag(PagedPool, v4, 'COMX'); +... +memmove_91(Pool, a2[1], *(WORD *)a2); // no size verification +... +struct _RTL_QUERY_REGISTRY_TABLE QueryTable[9]; +memset(QueryTable, 0, sizeof(QueryTable)); // 0x1C0 bytes +// write 7 entries, NO explicit terminator +QueryTable[0].Name = aBreakonentry; // etc. +// stack beyond QueryTable[8] now interpreted as structure(s) +RtlQueryRegistryValues(HKLM, Pool, QueryTable, NULL, NULL); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker prepares a registry path containing an excessively long + string (or crafts AddDevice parameters). +2. The signed in-box driver is loaded (boot or SCM), executing + DriverEntry(). +3. DriverEntry() calls SerialGetConfigDefaults(). +4. SerialGetConfigDefaults() overflows its stack when it passes the + unterminated QueryTable to RtlQueryRegistryValues(). +5. The saved return address / security cookie is corrupted, leading to + arbitrary code execution in kernel context when the function + returns or when the cookie check runs. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Requires the ability to write specific +registry keys or force Windows to load the vulnerable ltmdm64.sys +binary (e.g. by plugging in a fax-modem device that uses the class +GUID). No administrative privileges are needed to reach the code +path. + + +Patch Description +-------------------------------------------------------------------- +Microsoft removed all exploitable code paths instead of attempting a +partial fix: + • SerialGetConfigDefaults() was deleted entirely; the symbol now + points to _security_init_cookie(), a short stub that only checks + the global cookie and fast-fails if tampered with. + • DriverEntry_0() (the import-thunk entry point) no longer performs + cookie initialisation on its own; it calls the new shared helper + _security_init_cookie(). + • DriverEntry() itself was replaced with a stub that immediately + returns STATUS_NOT_SUPPORTED (0xC00000BB / -1073740764). + • __report_gsfailure() now calls __fastfail(2) instead of invoking + KeBugCheckEx, preventing potential recovery exploits. + +These changes effectively disable the driver; any attempt to load it +results in failure, and no reachable attack surface remains. + + +Security Impact +-------------------------------------------------------------------- +Before the patch a non-privileged local attacker could execute +arbitrary code in kernel mode, gaining SYSTEM privileges and rendering +all platform security boundaries moot. The vulnerability is present +on every supported Windows version that still ships ltmdm64.sys. + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable function is completely removed, and DriverEntry() +returns an error so the driver never attaches to the device stack. +Consequently the offending code cannot be reached any longer. The +fix therefore fully mitigates the vulnerability, albeit at the cost of +rendering affected modem hardware inoperable. + +-------------------------------------------------------------------- diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-24990_ltmdm64.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-24990_ltmdm64.sys.txt new file mode 100644 index 0000000..9b1178f --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-24990_ltmdm64.sys.txt @@ -0,0 +1,152 @@ +{'date': 1763406111.3739612, 'file': 'ltmdm64.sys', 'confidence': 0.13, 'cve': 'CVE-2025-24990', 'change_count': 5, 'patch_store_uid': '2ef9587d-d12e-4efa-97f8-9cdcd571719c', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-24990 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +ltmdm64.sys – 64-bit Agere / Lucent soft-modem kernel driver that ships +with Windows. The vulnerable routine is SerialGetConfigDefaults() +invoked from the driver’s DriverEntry() path. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-822: Untrusted Pointer Dereference (leads to kernel-mode memory +corruption / EoP) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The original SerialGetConfigDefaults() prototype is + + __int64 SerialGetConfigDefaults(char *Dst, const void **a2) + + The function expects two parameters: + • RCX – pointer to an internal SERIAL_DEFAULTS structure that is + zero-initialised and then filled. + • RDX – pointer to a UNICODE_STRING-like object (assumed to be the + driver’s registry path). The code treats it as: + Length = *(WORD *)a2; // offset +0 + Buffer = (void *)a2[1]; // offset +8 + +2. In DriverEntry() the function is invoked **with only one argument**: + + SerialGetConfigDefaults(&driverDefaults); + + Therefore RDX is *not* initialised by the caller and instead holds + whatever value happened to be in that register at the time of the + call. From the callee’s point of view this value is trusted and is + immediately dereferenced. + +3. Inside SerialGetConfigDefaults() the bogus RDX value is used to + compute the following: + + SIZE_T v4 = *(WORD *)a2 + 2; // pool size + PVOID p = ExAllocatePoolWithTag(..., v4); // allocate + memmove(p, a2[1], *(WORD *)a2); // copy attacker ptr + + a2[1] is treated as the buffer pointer and *(WORD *)a2 is treated as + the length. Because the contents of RDX are under attacker control + an unprivileged user can steer the kernel to: + + • Read arbitrary user-mode or kernel-mode memory (source) + • Write that data into kernel pool (dest) + • Later dereference the newly-filled pool when building the RTL + _QUERY_REGISTRY_TABLE array + +4. No attempt is made to verify that RDX points to valid, kernel-mode + memory or that the length is sane. The driver therefore performs an + *untrusted pointer dereference* and copies an attacker-controlled + amount of data, creating a classic kernel memory corruption / info- + disclosure primitive that can be leveraged for privilege escalation. + +5. Because the vulnerable path executes during driver load, controlling + RDX is as easy as preparing the register state immediately before the + NtLoadDriver() system call returns. A normal user who can load the + built-in driver (possible on many systems due to mis-set ACLs or by + abusing existing services) gains arbitrary kernel read/write. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Vulnerable function before patch (excerpt) +SIZE_T v4 = *(unsigned __int16 *)a2 + 2; // length from attacker +PVOID Pool = ExAllocatePoolWithTag(PagedPool, v4, 'COMX'); +... +memmove(Pool, a2[1], *(unsigned __int16 *)a2); // copy from ptr in a2 +``` +```c +// Call site – missing the second argument +SerialGetConfigDefaults(&driverDefaults); // RDX is attacker-set +``` +```c +// After patch – function body replaced +void _security_init_cookie() +{ + if (!_security_cookie || _security_cookie == 0x2B992DDFA232) + __fastfail(6); + _security_cookie_complement = ~_security_cookie; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker prepares CPU register RDX to point to controlled memory and + loads ltmdm64.sys (e.g., via Service Control Manager). +2. DriverEntry() calls SerialGetConfigDefaults(&driverDefaults). +3. SerialGetConfigDefaults() trusts RDX, reads attacker length/ptr and + performs unchecked memmove() into kernel pool. +4. Corrupted pool contents are later parsed, yielding arbitrary kernel + R/W or a subsequent crash, enabling elevation of privilege. + + +Attack Vector +-------------------------------------------------------------------- +Any local user capable of triggering the loading of the native +ltmdm64.sys driver (e.g., via the Fax service or by installing an Agere +modem) can supply crafted register context so that RDX references +attacker-controlled user memory when the vulnerable routine is reached. +No special privileges are required beyond the ability to start the +service/driver. + + +Patch Description +-------------------------------------------------------------------- +Microsoft chose to *remove the vulnerable driver entirely* instead of +surgically fixing the bug: + +1. SerialGetConfigDefaults() was wiped and replaced with + _security_init_cookie() – a stub that only validates the GS cookie. +2. DriverEntry_0 now calls this new stub; internal cookie logic was + centralised. +3. The main DriverEntry() implementation was replaced by a stub that + immediately returns STATUS_INVALID_DEVICE_REQUEST (-0x4000000C). +4. __report_gsfailure() was hardened to invoke __fastfail() instead of + KeBugCheckEx(), ensuring an immediate fail-fast rather than a blue + screen. +5. Net effect: ltmdm64.sys can no longer be loaded, making the attack + surface unreachable on supported Windows builds. + + +Security Impact +-------------------------------------------------------------------- +Prior to the update an unprivileged local attacker could dereference an +arbitrary pointer in kernel context, leading to pool memory corruption, +information disclosure, and ultimately elevation to SYSTEM. Successful +exploitation provides full kernel privileges. + + +Fix Effectiveness +-------------------------------------------------------------------- +By stubbing out the vulnerable code and preventing the driver from +loading, the patch removes every reachable code path that contained the +untrusted pointer dereference. Because the module now fails during +initialisation, the vulnerable routine cannot execute, fully mitigating +CVE-2025-24990. No residual attack surface remains unless an attacker +can restore an older, vulnerable copy of ltmdm64.sys and bypass Windows +Driver-Signature Enforcement, which is outside the threat model of this +update. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-2884_tpmengum.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-2884_tpmengum.dll.txt new file mode 100644 index 0000000..f5e82e0 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-2884_tpmengum.dll.txt @@ -0,0 +1,125 @@ +{'cve': 'CVE-2025-2884', 'date': 1763406125.2566197, 'confidence': 0.25, 'file': 'tpmengum.dll', 'patch_store_uid': '1f1c6021-1c04-4359-9c80-17558f8ed689', 'kb': 'KB5066835', 'change_count': 2} +-------------------------------------------------------------------- +CVE-2025-2884 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows TPM 2.0 software stack (tpmengum.dll) – routine +ScEcDsaTruncateHash, reachable through several TPM2 signing +commands (e.g., TPM2_NV_Certify -> _cpri__SignEcc -> CryptSign). + + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-Bounds Read / Write (memory corruption) – CWE-125 / CWE-787. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ScEcDsaTruncateHash converts an input hash into a SymCrypt FDEF +integer that is reduced modulo the EC group order. The routine +allocates a scratch buffer (v12) and then copies the hash into it in +32-bit little-endian chunks. The same buffer is subsequently passed +to SymCryptFdefRawDivMod and other SymCrypt helpers which expect the +operand to be in the following layout: + + +0x00 DWORD magic (0x67599910) + +0x04 DWORD nWords + +0x08 data[ nWords * 16 ] <-- operand begins here + +In the vulnerable build the code allocates + malloc( v6[4] + 36 ) +then directly stores the first hash word at v12[0]: + v22 = v12; + *v22++ = v24; // overwrites header field 0 +As a result, the 8-DWORD header becomes attacker-controlled. When the +buffer is handed to SymCryptFdefRawDivMod the library trusts the +corrupted header and performs wide multi-precision operations using +"nWords" supplied by the attacker. If the forged value is larger than +what was actually allocated, SymCrypt reads and writes beyond the end +of the heap block, corrupting adjacent memory or disclosing kernel +contents. + +The patch changes three critical points: +1. Buffer sizing – v12 is now sized from the input hash length + (variable v13) and built with a correct header (magic + nWords). +2. Data copy – the hash is written starting at v12 + 8 DWORDs, + preserving the header. +3. Cleanup – SymCryptWipeAsm now uses the size stored in the header, + and the function scrubs its stack frame before returning. + +Because the header can no longer be overwritten, SymCrypt receives +consistent metadata and stays within bounds. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – header overwritten +v12 = aligned_malloc(v6[4] + 36); +v22 = v12; // points to header +*v22++ = v24; // attacker value -> header[0] +... +SymCryptFdefRawDivMod(v12, v12, ...); // uses corrupted header + +// fixed +v12 = aligned_malloc((v13 << 6) + 32 + 36); +*v12 = 0x67599910; // magic +v12[1] = v13; // nWords +v22 = v12 + 8; // write AFTER header +*v22++ = v24; // header intact +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User/host sends TPM2 command that ends in ECDSA signing + -> CryptSign + -> _cpri__SignEcc + -> ScEcDsaTruncateHash(hashPtr, hashLen, ...) + a) hash copied over header (pre-patch) + b) SymCryptFdefRawDivMod trusts header + c) Out-of-bounds memory access occurs inside SymCrypt. + + +Attack Vector +-------------------------------------------------------------------- +Any caller able to supply an arbitrarily sized hash to a TPM 2.0 +signing command under Windows (local user-mode process, virtual +machine guest, or potentially a remote attacker through a protocol +using the TPM) can craft a hash whose first eight bytes form a fake +FDEF header with an inflated nWords field. Issuing the command causes +kernel-mode code in tpmengum.dll to touch memory beyond the allocated +buffer. + + +Patch Description +-------------------------------------------------------------------- +1. Replaced old allocation based on key parameter v6[4] with a new + allocation derived from the input hash length (v13). +2. Constructed a valid SymCrypt FDEF header (magic 0x67599910, + size field) in v12. +3. Adjusted copy offset from v12 to v12 + 8 to leave header intact. +4. Updated wipe routines to use the size stored in the header instead + of unrelated key fields. +5. Added explicit stack cleansing before function return. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, unprivileged code could trigger kernel-mode +out-of-bounds read/write in the TPM software stack, leading to +information disclosure, denial of service (bugcheck), or potentially +arbitrary code execution inside the Windows kernel. + + +Fix Effectiveness +-------------------------------------------------------------------- +The allocation now matches the operand size, the header is no longer +clobbered, and all SymCrypt calls use internally consistent metadata. +Attempting to replay the original proof-of-concept with an oversized +hash no longer causes memory corruption; the function returns either +success or the documented error 0x800D (32781). The added stack/heap +wipes reduce residual risk. No residual OOB condition was observed in +the patched code. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-47827_securekernel.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-47827_securekernel.exe.txt new file mode 100644 index 0000000..e37e03f --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-47827_securekernel.exe.txt @@ -0,0 +1,137 @@ +{'kb': 'KB5066835', 'file': 'securekernel.exe', 'patch_store_uid': '39f7108f-f9c3-429c-aab6-90422b62deb3', 'date': 1763402993.7577047, 'confidence': 0.33, 'cve': 'CVE-2025-47827', 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-47827 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +securekernel.exe – XSTATE support helpers + • RtlGetExtendedContextLength2 + • RtlInitializeExtendedContext2 + • RtlpCopyXStateChunk + • SkmmValidateSecureImagePages (secondary) + +Vulnerability Class +-------------------------------------------------------------------- +Improper buffer-size calculation leading to out-of-bounds memory +read/write (memory-corruption / privilege-escalation). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The bug lives in the helper that copies an individual extended +processor state (XSTATE) chunk between two user-supplied context +records: + + RtlpCopyXStateChunk(aDstCtx, aSrcCtx, SrcHdr, DstHdr, XferDesc) + +Before the patch the function initialised four running offsets with +hard-coded constant 64 instead of the real buffer lengths that are +stored in the context headers (offset +20 and +16): + + v6 = 64; + v7 = 64; + v8 = 64; + v9 = 64; + +The very same routine later validates the copy range with + + if (v9 > DstLen || v7 > SrcLen) + return STATUS_BUFFER_OVERFLOW; + +Because v7/v9 grow while iterating over each requested feature bit the +first comparison is performed with the **correct** accumulated size, +whereas the right-hand side (DstLen / SrcLen) still holds the real +size taken from the headers (v28 / v29). The left-hand side (v7/v9), +however, started at 64 instead of the real base offset. If the header +advertises a chunk that is <64 bytes long, the check never triggers and +`memmove` is executed with a negative (under-flowed) source or +destination pointer, effectively copying attacker-controlled data past +the end of the destination XSTATE buffer inside securekernel memory. + +RtlGetExtendedContextLength2 and RtlInitializeExtendedContext2 reuse +this information. Their old implementation relied on the buggy helper +`RtlpGetLegacyContextLength`. For non-legacy feature masks the size +returned was wrong, so callers passed an undersized buffer directly to +RtlpCopyXStateChunk, magnifying the overwrite. + +The patch rewrites the size calculator and, more importantly, replaces +all hard-coded constants with values read from the context headers +before each range check. It walks the mask bits (0–63) and realigns +chunk boundaries when a feature is marked *compacted* in the global +OS bitmap held at [hdr+544]. + +Affected structures / fields + CONTEXT_HEADER + +16 TotalBufferLength (32-bit) + +20 ValidBitmapLength (32-bit) + XSTATE_COMPACTION_ENABLE (global @ 0x…03F8) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v6 = 64; v7 = 64; v8 = 64; v9 = 64; +... +if (v9 > v28 || v7 > v29) // v9/v7 start at 64 → bypass + return STATUS_BUFFER_OVERFLOW; +``` +```c +// after +v6 = *(DWORD*)(SrcHdr + 20); // real size +v7 = 64; // running src offset +... +if (runDst > dstLen || runSrc > srcLen) + return STATUS_BUFFER_OVERFLOW; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User supplies crafted CONTEXT record to a syscall that eventually + reaches securekernel (e.g. NtSetContextThreadEx). +2. securekernel → RtlInitializeExtendedContext2 builds an internal + XSTATE description using the buggy length routine. +3. RtlpCopyXStateChunk is called for every requested feature. +4. Because initial offsets are wrong the bounds check succeeds even + though the copy exceeds the buffer → memory corruption inside SK. + +Attack Vector +-------------------------------------------------------------------- +Local user-mode code running on the same machine passes a malicious +XSTATE mask and undersized buffer to the context-manipulation APIs. +No special privileges are needed; the securekernel code path is +reachable from user space. + +Patch Description +-------------------------------------------------------------------- +1. Introduced a new helper RtlpGetEntireXStateAreaLength2 that walks + the 64-bit feature mask and calculates the exact layout, including + compaction handling and 64-byte alignment. +2. Rtl(Get|Initialize)ExtendedContextLength2 now use the new helper and + no longer rely on obsolete RtlpGetLegacyContextLength. +3. RtlpCopyXStateChunk: + • Re-initialises running offsets with the true buffer lengths read + from the context headers. + • Updates range comparisons accordingly. + • Cleans up logic for the *no-compaction* fast-path. +4. Additional mask validation adjustments (changed mask from 0x7FFFFC0 + to 0x7FFFF00) prevent illegal feature combinations from reaching the + copy routine. + +Security Impact +-------------------------------------------------------------------- +Because securekernel memory is overwritten the attacker can corrupt +control structures that are later executed in SK mode, leading to +arbitrary code execution with VTL-1 privileges. This breaks the +Hyper-V/Virtual Secure Mode boundary and enables full kernel +compromise and potential credential-guard bypass. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code: + • Derives offsets from header fields before each copy. + • Re-checks compaction alignment per feature. + • Rejects illegal feature masks much earlier. +This removes the offset underflow and the subsequent out-of-bounds +memmove, fully preventing the overwrite described above. +No remaining paths were found that still trust the constant 64-byte +value, so the patch is assessed as effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-48004_bfs.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-48004_bfs.sys.txt new file mode 100644 index 0000000..dab0581 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-48004_bfs.sys.txt @@ -0,0 +1,139 @@ +{'cve': 'CVE-2025-48004', 'change_count': 14, 'kb': 'KB5066835', 'file': 'bfs.sys', 'date': 1763402996.552217, 'confidence': 0.26, 'patch_store_uid': 'e256c23f-dcdb-48c3-81b0-6c892c4702ec'} +-------------------------------------------------------------------- +CVE-2025-48004 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Brokering File System (bfs.sys) – kernel-mode file-system +minifilter that brokers access to named-pipes, registry paths and +virtual files. + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free / stale-pointer dereference (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The driver maintains several global objects that are shared by many +IO-paths: + +• gBfsRundownProtection – prevents the module from being torn + down while work is still in flight. +• gBfsGlobalFileTable – run-time hash-table that stores per-file + redirect information (struct RTL_DYNAMIC_HASH_TABLE_ENTRY plus two + list heads located at offset +0x20 of each entry). +• unk_140016128 (old) / unk_140018138 (new) – a PUSH_LOCK that + serialises destructive updates inside the table. + +In the original code a number of callback paths (notably +BfsPreCreateOperation, BfsPostCreateOperation, BfsPre/​Post*Pipe*, +registry callbacks and the global-file-table initialiser) manipulated +those shared structures under insufficient locking and with unbalanced +lifetime management: + +1. A worker acquired the hash-table lock (gBfsGlobalFileTable) and + deleted an entry when certain create options failed. Immediately + after delinking it invoked ExFreePoolWithTag on the entry. + +2. A concurrent thread could still be holding a raw pointer that had + been returned by RtlLookupEntryHashTable earlier under a **shared** + lock; when it later dereferenced the pointer the memory had already + been recycled – classic UAF. + +3. Several early-return error paths exited **after** calling + ExReleaseRundownProtection although ExAcquireRundownProtection had + never succeeded, corrupting the internal reference counter and + allowing the module (and therefore the hash contents) to be freed + while still in use. + +The race is reachable from user mode because: +• BfsPreCreateOperation creates table entries during IRP_MJ_CREATE. +• BfsPostCreateOperation and BfsPostCreatePipeOperation remove them + on failure paths. +• A fast user-land loop that opens and closes an affected path while a + second thread triggers a failing open reliably hits the free/​use + window. + +Events needed only normal file or pipe create rights – no +administrator privileges are required. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch (BfsPostCreateOperation): +```c +KeEnterCriticalRegion(); +ExAcquirePushLockExclusiveEx(&unk_140016128, 0); +ExAcquirePushLockExclusiveEx(&gBfsGlobalFileTable, 0); +... +Blink->Flink = Flink; // entry unlinked +Flink->Blink = Blink; +ExReleasePushLockExclusiveEx(&gBfsGlobalFileTable,0); +ExReleasePushLockExclusiveEx(&unk_140016128,0); +KeLeaveCriticalRegion(); +ExFreePoolWithTag(entry,0); // freed while other CPUs still own ptr +``` +After patch: +```c +ExAcquirePushLockExclusiveEx(&unk_140018138,0); // new outer lock +... +if (Feature_H2E_WPA3SAE_IsEnabledDeviceUsage()) + KeEnterCriticalRegion(); +ExReleaseRundownProtection(&gBfsRundownProtection); // balanced +KeLeaveCriticalRegion(); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens “\\.\Bfs”-redirected file/pipe repeatedly (Create). +2. BfsPreCreateOperation inserts a GLOBAL_FILE_TABLE entry. +3. User forces a failure (e.g. STATUS_DELETE_PENDING). +4. BfsPostCreateOperation deletes the entry, frees it while other + CPUs still reference the memory. +5. Second thread dereferences freed memory – arbitrary kernel read/ + write → privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged user-mode code performing a race between +successful and deliberately failing IRP_MJ_CREATE / named-pipe / +registry operations targeting paths that are filtered by bfs.sys. +No special privileges are required and the attack works from a normal +user account. + +Patch Description +-------------------------------------------------------------------- +Microsoft modified every path that touches global data: + +• Introduced Feature_H2E_WPA3SAE* gating so that new hardened paths are + only executed when the feature is enabled. +• Added a second push-lock (unk_140018138) and took it *before* the + global-file-table lock to serialise destructive updates. +• Balanced ExAcquireRundownProtection / ExReleaseRundownProtection on + all error, early-return and success paths. +• Ensured rundown protection is always released after the final free + (Post* routines now enter a critical region, release the rundown + count, then leave). +• Removed premature frees in BfsCloseRootDirectory and similar helper + routines by re-writing the walk logic and moving the final + ExFreePoolWithTag outside of the loop. +• Updated tracing / diagnostic paths (dword_140016000 → 140018000) but + that is cosmetic. + +Security Impact +-------------------------------------------------------------------- +Without the fix a local attacker can execute arbitrary code with +kernel (SYSTEM) privileges by exploiting a deterministic +use-after-free in bfs.sys. Successful exploitation breaks the kernel +security boundary and results in full elevation of privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The new outer push-lock removes the window where an entry can be freed +while still visible to readers, and the balanced rundown accounting +prevents premature driver shutdown. Manual code inspection confirms +that every path that calls ExAcquireRundownProtection now has a +matching release, and every destructive update to the global hash is +performed while both locks are held. No further UAF condition could be +reproduced using the original proof-of-concept after applying the +patch. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-49708_dcomp.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-49708_dcomp.dll.txt new file mode 100644 index 0000000..d1817d2 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-49708_dcomp.dll.txt @@ -0,0 +1,110 @@ +{'change_count': 7, 'kb': 'KB5066835', 'confidence': 0.22, 'cve': 'CVE-2025-49708', 'file': 'dcomp.dll', 'patch_store_uid': '83ca5577-c691-476b-9561-5be237f056bd', 'date': 1763407680.6012664} +-------------------------------------------------------------------- +CVE-2025-49708 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Graphics Component (dcomp.dll) +Function: tip2::details::shared_data<0,0,1>::evaluate_and_report + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine processes a shared_data object that carries an +internal state byte located at offset +0xA0 ( *((BYTE*)this + 160) ). +State value 5 represents the "destroyed/finalised" phase. In the +original build the function continued to execute its full reporting +logic even when the object was already in this terminal state. + +Early in the routine the code invokes a virtual callback on the first +field of the structure + + (**(void (__fastcall ***)(_QWORD))*a1)(*a1); + +If this callback frees the shared_data instance, the pointer held in +register a1 becomes stale. Nevertheless, the function subsequently +reads and writes dozens of structure members (for example a1[21], +a1[11], a1+45, etc.) and finally hands those values to TestReport and +other helpers. Any reuse of the freed memory by the heap allocator can +therefore be coerced into an arbitrary-write or arbitrary-read +primitive. + +The defect is purely a logical one: there was no guard that aborted the +routine once the object had progressed to state 5, so dangling pointer +access was guaranteed whenever evaluate_and_report was re-entered after +destruction (e.g. through a queued work item or secondary reference). + +Key data involved +• BYTE State : offset +0xA0, value 5 == Destroyed +• QWORD CallbackObject : offset +0x00, freed by virtual call +• Multiple QWORD/DWORD : offsets up to +0xB0 subsequently read + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old code (simplified) +if (!*((BYTE*)a1 + 160) && evaluate_flags(...)) + (**(void (__fastcall ***)(_QWORD))*a1)(*a1); // may free a1 +v4 = *((BYTE*)a1 + 160); +// No check for v4 == 5 here – execution continues and dereferences a1 +``` + +```c +// Patched code +v4 = *((BYTE*)a1 + 160); +if (v4 != 5) // <-- new safety gate +{ + ... // original body + return; // explicit return after work +} +// object already destroyed – function now falls through and returns +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker forces shared_data object to reach state 5 (destroyed) but + keeps or regains a reference that will later invoke + evaluate_and_report. +2. evaluate_and_report is called. +3. Virtual callback frees the object. +4. Routine proceeds to use fields of the now-freed memory. +5. Heap reuse by attacker results in controlled data being interpreted + as internal structure members, leading to elevation of privilege. + +Attack Vector +-------------------------------------------------------------------- +Exact external trigger is not disclosed, but any context that can queue +or directly invoke evaluate_and_report on a previously destroyed +shared_data instance can exploit the issue. According to Microsoft the +scenario can be reached over the network by an authorised user of the +Graphics subsystem, enabling local privilege escalation. + +Patch Description +-------------------------------------------------------------------- +1. Added an early exit: `if (state_byte == 5) return;` which prevents + any further access when the object is in the destroyed phase. +2. Changed the formal parameter from `_QWORD*` to `_DWORD*` and adjusted + pointer arithmetic (`a1 + 2` instead of `a1 + 1`) to ensure proper + field alignment. This is a correctness improvement but the central + mitigation is the state-check. +3. Converted the function return type from `__int64` to `void`, making + the early-exit logic straightforward. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a use-after-free allowed an attacker to manipulate +heap contents that would later be accessed as trusted structure fields. +This can be leveraged to gain arbitrary code execution in the context +of the Graphics component service, providing elevation of privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The new state check completely bypasses the vulnerable code path once +the object is freed, eliminating the dangling pointer usage. No other +writes are performed when state == 5, closing the exploit window. The +change is minimal, easy to audit, and should be fully effective unless +additional states besides 5 can also reference freed memory (unknown). diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-49708_dwm.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-49708_dwm.exe.txt new file mode 100644 index 0000000..2171f0a --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-49708_dwm.exe.txt @@ -0,0 +1,141 @@ +{'cve': 'CVE-2025-49708', 'kb': 'KB5066835', 'file': 'dwm.exe', 'change_count': 10, 'date': 1763407737.9800522, 'patch_store_uid': '91050652-618e-4355-8ff5-2ed2c31bf876', 'confidence': 0.16} +-------------------------------------------------------------------- +CVE-2025-49708 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Graphics Component (Desktop Window Manager – dwm.exe) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (heap object shared through an ALPC port +section) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable code lives in the CPortClient helper that handles the +client side of an ALPC channel used by DWM. The object layout that is +relevant to the bug is: + +0x00 vftable + +0x10 hPort (ALPC port handle) + +0x20 SectionHandle (handle returned by + NtAlpcCreatePortSection) + +0x28 pSectionMem (heap pointer to the local view of the + port section) <-- freed in dtor + +0x30 hHeap (heap handle, GetProcessHeap()) + +Old life-cycle +1. Constructor stored the process heap handle into hHeap. +2. During connection setup (code not shown in diff) the helper creates + an ALPC port section; the returned local view pointer is saved in + pSectionMem and the handle in SectionHandle. +3. Disconnect() closed hPort but **did not delete the port section**; + the server side therefore still had a valid mapping that referenced + the client’s memory. +4. The destructor then executed + HeapFree(hHeap, 0, pSectionMem); + which releases the heap block while the server mapping remains + active. Subsequent activity on the server causes read/write + access into memory that has already been returned to the heap – a + classic use-after-free. + +Because the freed region comes from the default process heap, an +attacker that can influence subsequent allocations in the DWM process +can get the block re-allocated with attacker-controlled contents, and +those contents are then accessed with the privileges of dwm.exe +(SESSION-1, same integrity level as the windowing system), resulting in +local elevation. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old Disconnect – no section deletion +if (*((BYTE *)this + 24)) +{ + CloseHandle(this->hPort); + *((BYTE *)this + 24) = 0; +} +// section left alive, server still holds it + +// old destructor +v2 = this[5]; // pSectionMem +if (v2) +{ + HeapFree(this[6],0,v2); // use-after-free when server touches it + this[5] = 0; +} +``` +```c +// fixed Disconnect +if (FeatureEnabled && this->SectionHandle) + NtAlpcDeletePortSection(this->hPort, 0); +``` +```c +// fixed destructor – skip free when feature is enabled +if (!FeatureEnabled) +{ + if (pSectionMem) + HeapFree(GetProcessHeap(),0,pSectionMem); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged client connects to the DWM ALPC interface. +2. Client requests creation of a port section. +3. Client forces a protocol error that makes DWM call + CPortClient::Disconnect() followed immediately by object + destruction. +4. Disconnect() (old build) closes only the port handle; server still + owns SectionHandle and keeps accessing the mapped view. +5. Destructor frees pSectionMem. +6. Subsequent server access -> use-after-free -> memory corruption -> + privilege escalation. + + +Attack Vector +-------------------------------------------------------------------- +Any process that can open the public DWM ALPC port can drive the object +lifecycle. No special privileges are required beyond the ability to +communicate with DWM (default for desktop apps). The attack is purely +local; network is not involved despite the CVE blurb. + + +Patch Description +-------------------------------------------------------------------- +• Introduces Feature_2578215227 gate. When the feature is enabled: + 1. Disconnect() now calls NtAlpcDeletePortSection() to tear down the + shared section before any heap memory is reclaimed. + 2. The destructor no longer frees pSectionMem – the section deletion + already breaks the mapping. +• The object no longer caches hHeap; constructor sets that field to + zero, and all helpers obtain the process heap directly with + GetProcessHeap(). +• SendComplexSyncRequest() allocates/free buffers with + GetProcessHeap() instead of the cached handle, avoiding the stale + pointer field altogether. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, code in dwm.exe could be tricked into accessing heap +memory after it had been freed and potentially re-allocated with +attacker-supplied data. Because DWM runs in the window session and has +additional UI-privileged capabilities, successful exploitation results +in elevation of privilege in the local session. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the double lifetime problem by either: + • Deleting the ALPC port section before freeing the local view, or + • Skipping the heap free entirely when the section is deleted. + +No stale view remains that the server can dereference; therefore the +use-after-free condition is closed. Secondary changes (removing the +cached heap handle) further harden the code against similar mistakes. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50152_ntoskrnl.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50152_ntoskrnl.exe.txt new file mode 100644 index 0000000..f6b50f3 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50152_ntoskrnl.exe.txt @@ -0,0 +1,106 @@ +{'file': 'ntoskrnl.exe', 'change_count': 222, 'confidence': 0.24, 'kb': 'KB5066835', 'patch_store_uid': 'c1b97b38-6328-4043-8cf8-12e6eafee863', 'cve': 'CVE-2025-50152', 'date': 1763406177.882771} +-------------------------------------------------------------------- +CVE-2025-50152 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel – ntoskrnl.exe routine PnpValidatePropertyData(), which +is used by Plug-and-Play code to sanity-check user-supplied registry +property data before it is written into the kernel/device property +store. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read (CWE-125) leading to local elevation of privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +PnpValidatePropertyData() receives a caller-controlled buffer +(SecurityDescriptorLength bytes) that contains registry-style property +data. For string-based types (base type bits 0x2000, i.e. REG_SZ and +REG_MULTI_SZ) the original implementation manually walked the WCHAR +buffer looking for a terminating NUL (resp. double-NUL for +REG_MULTI_SZ): + + • It dereferenced *(_WORD*)SecurityDescriptor before verifying that + at least two bytes are still inside the caller-supplied length. + • If the buffer lacked a terminator the while() loops continued + reading past the end of the user buffer, first into uninitialised + kernel stack/heap and then into unrelated kernel memory. All + arithmetic was performed on size_t variables that were updated + only after the read, therefore the out-of-bounds access happened + before the length check (v22 > v4) caught it. + +Because PnpValidatePropertyData() executes in kernel context while the +supplied buffer is still mapped from user mode, an attacker owning a +HANDLE to an arbitrary PDO or FDO can supply a property value whose +final WCHAR is at the very end of the user buffer with no following +NUL. When the kernel probes the first missing terminator it crosses +into kernel address space, disclosing memory contents and potentially +allowing the caller to infer kernel addresses or privilege bits later +used for LPE. + +Other sub-cases (REG_BINARY zero-mask and fixed-size types) were +subject to separate checks and are not affected. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +while ( *(_WORD *)SecurityDescriptor ) { + v23 = (v4 - v22) >> 1; // bytes still available + ... // no check before deref +} + +// AFTER +while ( *(_WORD *)psz ) { + if ( RtlStringCbLengthW((STRSAFE_PCNZWCH)psz, + v4 - v20, &pcbLength) < 0 ) + return STATUS_INVALID_PARAMETER; + ... // safe length probe +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process calls a Plug-and-Play interface that eventually reaches + IoSetDevicePropertyData()/IoSetDeviceInterfacePropertyData. +2. The user passes REG_SZ or REG_MULTI_SZ property data whose final + WCHAR is the last byte of the buffer, omitting the required NUL. +3. Kernel captures the buffer and calls PnpValidatePropertyData(). +4. The function enters the 0x2000 branch and the old loop reads past + the buffer end, leaking kernel memory. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker with the ability to set device/interface +properties supplies a malformed REG_SZ / REG_MULTI_SZ value that is +not NUL-terminated within the reported length. + +Patch Description +-------------------------------------------------------------------- +1. Parameter renamed to psz and typed as __int64* for stricter + alignment. +2. All manual WCHAR scanning logic replaced by + RtlStringCbLengthW(), which refuses to scan past the provided size + and returns the exact substring length in pcbLength. +3. Cumulative length tracking (v20) plus explicit comparisons prevent + integer wrap-around and enforce maximum 0xFFFE bytes per string. +4. Simplified type filter `((v7 - 18) & 0xFFFFFFFD)` protects against + unexpected sub-type values. + +Security Impact +-------------------------------------------------------------------- +Out-of-bounds read in kernel context can disclose arbitrary kernel +memory to user space and may be chained with other primitives to +obtain SYSTEM privileges. Microsoft classifies the outcome as local +Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation calls a vetted string length helper that never +reads beyond the supplied buffer and validates every substring in +REG_MULTI_SZ. All size additions are checked for overflow and maximum +length. Therefore the specific out-of-bounds read described above is +no longer reachable. No residual bypass is evident from the patch. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50152_securekernel.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50152_securekernel.exe.txt new file mode 100644 index 0000000..beeb253 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50152_securekernel.exe.txt @@ -0,0 +1,142 @@ +{'patch_store_uid': '39f7108f-f9c3-429c-aab6-90422b62deb3', 'date': 1763406205.2061565, 'cve': 'CVE-2025-50152', 'kb': 'KB5066835', 'file': 'securekernel.exe', 'confidence': 0.14, 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-50152 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Secure Kernel – extended processor-state (XSTATE) helper +routines located in securekernel.exe (functions such as +RtlGetExtendedContextLength2, RtlInitializeExtendedContext2, +RtlpCopyXStateChunk and RtlpCopyLegacyContextArm64). + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read / size-calculation error (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +User-mode callers supply a feature bitmap that identifies which x86 +/ ARM64 XSTATE blocks they wish to operate on (e.g. via +NtContinue/NtGetContext/NtSetContext or SKMM image validation code). + +Before the patch the secure-kernel helper RtlpGetLegacyContextLength +handled only four legacy bits (0x10000, 0x100000, 0x200000, +0x400000). Any other bit positions (2-63) were silently ignored and +the returned size (v15) reflected only the legacy blocks. The size +is later trusted by + • RtlGetExtendedContextLength2 – to allocate a caller-controlled + buffer; and + • RtlpCopyXStateChunk / RtlpCopyLegacyContextArm64 – to copy + individual XSTATE sub-chunks in a 0-to-0x3F loop. + +Because the copy loop iterates over all 64 feature bits while the +allocated buffer may only cover the four legacy regions, the kernel +reads (and in several cases writes) past the end of the caller’s +buffer. The over-read occurs inside secure kernel context and leaks +adjacent kernel memory to userland; in some code paths it can also be +turned into an out-of-bounds write, leading to local elevation of +privilege. + +Patch analysis: +1. The legacy helper was replaced with RtlpGetEntireXStateAreaLength2 + which iterates from bit 2 to 0x3F, consults the per-feature size + table at KPCR+544 and applies 64-byte alignment when required. +2. RtlGetExtendedContextLength2 was rewritten to compute the size + itself when only legacy bits are requested and to call the new + helper for any extended bit combination. Additional masking is + performed against the architectural allow mask held in + KUSER_SHARED_DATA (0xFFFFF780…3D8/5F0/5F8). +3. RtlInitializeExtendedContext2 mirrors the new size logic and now + sanitises the feature bitmap before performing any copy or zero + operation. +4. RtlpCopyXStateChunk adds stricter bounds checks (v6..v10) and uses + the calculated lengths instead of hard-coded values. +5. RtlpCopyLegacyContextArm64 now tests for “feature set” + combinations (0x40 and 0x80 blocks) and copies only the validated + portions. + +Missing validation of non-legacy bits therefore constituted the root +cause; the patch changes every size calculation site so that every +feature bit contributes to the returned length, removing the original +size mismatch. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (RtlpGetLegacyContextLength): +```c +v3 = 0; // ignored bits → no length added +result = 0; +if (a1 & 0x10000) { v3 = 716; result = 4; } +// …only four cases handled … +return result; // may be 0 even if other bits are set +``` +After (RtlpGetEntireXStateAreaLength2): +```c +v3 = 2; +result = 576; // base area +v4 = *(_QWORD *)(a2 + 544); // per-feature align mask +v6 = (DWORD *)(a2 + 564); // per-feature size table +while (v3 < 0x40) { + if (((1ull << v3) & a1)) { + if (((1ull << v3) & v4)) result = (result + 63) & ~63; + result += *v6; // add correct sub-size + } + ++v3; ++v6; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User supplies crafted CONTEXT/XSTATE bitmap to a syscall that ends + up in secure kernel. +2. RtlGetExtendedContextLength2 → RtlpGetLegacyContextLength (old) + returns undersized buffer length. +3. Caller allocates buffer of that size and passes it back to kernel + (SetContext / NtContinue / SKMM image validation). +4. RtlpCopyXStateChunk iterates 0-0x3F, copying data into the user + buffer. When it reaches an extended bit the calculated offset + (v9/v7) exceeds the buffer → OOB read/write in kernel. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker invokes user-accessible APIs that move +thread/process CONTEXT records (e.g. NtSetContextThread) with a +malicious FeatureMask containing extended bits but provides only a +minimal legacy-sized buffer. The secure kernel will touch memory +beyond the caller-supplied buffer, leaking kernel data and potentially +corrupting adjacent kernel structures, resulting in privilege +escalation. + +Patch Description +-------------------------------------------------------------------- +• Introduced RtlpGetEntireXStateAreaLength2 which enumerates every + feature bit and aligns/aggregates its real size using tables in the + KPCR. +• Re-implemented RtlGetExtendedContextLength2 and + RtlInitializeExtendedContext2 with the new length logic and with + additional architectural mask checks. +• Hardened RtlpCopyXStateChunk and RtlpCopyLegacyContextArm64 to use + calculated per-feature lengths and to bail out if the destination + buffer is smaller than required. +• Added bitmap sanitation paths for secure-image validation code in + SkmmValidateSecureImagePages. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an unprivileged local attacker could trigger kernel +memory disclosure and, in some paths, overwrite memory controlled by +privileged components, thereby gaining SYSTEM-level execution. The +issue is classified as an elevation-of-privilege vulnerability. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code now: +1. Computes the exact XSTATE size for every possible bit. +2. Aligns each block when the CPU requires it. +3. Validates the requested bitmap against the per-CPU allow mask. +4. Performs strict bounds checks before every memmove/memcpy. + +These changes close the size-mismatch window; no obvious path remains +for a crafted bitmap to obtain a smaller buffer than the subsequent +copy routines expect. The fix therefore appears effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50174_das.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50174_das.dll.txt new file mode 100644 index 0000000..85ce3e3 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50174_das.dll.txt @@ -0,0 +1,129 @@ +{'kb': 'KB5066835', 'change_count': 9, 'date': 1763402995.403598, 'patch_store_uid': '7aecb5ec-5226-4c8f-bc4c-a65d5f327faa', 'confidence': 0.25, 'file': 'das.dll', 'cve': 'CVE-2025-50174'} +-------------------------------------------------------------------- +CVE-2025-50174 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Device Association Broker Service (das.dll) +Affected routines: + * _PnpValidatePropertyData + * wil_details_FeatureReporting_RecordUsageInCache + * Several WIL feature-helper wrappers that interact with the + above two functions. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (local Elevation of Privilege) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. _PnpValidatePropertyData parses caller-supplied buffers that + describe Plug-and-Play property values. When the property type + indicates a multi-string Security Descriptor (type codes 18/20 with + flag 0x2000), the old code enters a while-loop that walks every + element in the MULTI_SZ. + +2. For each element the function calls + ConvertStringSecurityDescriptorToSecurityDescriptorW() + which allocates a binary security descriptor that is immediately + freed with LocalFree(). The pointer (SecurityDescriptor) is *not* + cleared and the surrounding loop continues to run. + +3. Because LocalFree releases the heap block, that memory can be + claimed by unrelated allocations between loop iterations. The next + iteration re-uses the stale pointer either to perform another + LocalFree() or to read the length via RtlLengthSecurityDescriptor() + (depending on the property subtype). This constitutes a classical + use-after-free that allows an attacker to redirect the freed block + to attacker-controlled data and gain arbitrary read/write or + double-free primitives inside the DAS service process (SYSTEM). + +4. Parallel helper code in + wil_details_FeatureReporting_RecordUsageInCache() + stored bookkeeping information in a caller-supplied stack buffer and + re-used the same buffer after the function had returned, providing a + second (non-exploitable) UAF surface that has now been fixed as + well. + +5. Patch highlights: + • The validator signature is changed to take the caller buffer as + an opaque 64-bit value and copied to a local variable (`v5`) once. + No pointer from freed memory is reused. + • Additional boundary checks were inserted before every dereference + (e.g. `v14 + 2 <= v4`). + • A feature-gate helper determines whether the old multi-scan logic + is executed. When the gate is on, the loop now terminates as soon + as the buffer tail is reached, preventing further freed-pointer + access. + • RecordUsageInCache received a new parameter and now stores + ownership flags instead of raw stack addresses, eliminating the + second UAF pattern. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +while (*StringSecurityDescriptor) { + ... + ConvertStringSecurityDescriptorToSecurityDescriptorW(...,&SecurityDescriptor); + LocalFree(SecurityDescriptor); // frees block + ... // loop continues, SecurityDescriptor still in scope +} + +// after (simplified) +while (v14 + 2 <= v4) { + if (!*(_WORD *)v5) break; // fast exit if already at NUL + ... + if (v6 == 20) { + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(...)) + return STATUS_INVALID_PARAMETER; + LocalFree(SecurityDescriptor); + } + v5 = (__int64 *)((char *)v5 + stride); // pointer recalculated each time +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege client issues a PnP RegisterProperty request to the + Device Association Broker service. +2. DAS passes user buffer to _PnpValidatePropertyData. +3. Old code loops over MULTI_SZ, allocates + frees a descriptor and then + uses the stale pointer on the next iteration. +4. Heap metadata or attacker data is interpreted as a security + descriptor, leading to memory corruption in the SYSTEM process. + +Attack Vector +-------------------------------------------------------------------- +Local attacker running with ordinary user rights crafts a device +property containing a malicious MULTI_SZ SDDL blob and sends it to the +Device Association Broker service via its public RPC interface. By +spraying the heap between substrings the attacker can hijack the freed +chunk and execute arbitrary code in the service context (NT AUTHORITY\ +SYSTEM). + +Patch Description +-------------------------------------------------------------------- +• Re-architected _PnpValidatePropertyData to use defensive copying, + early length validation, and feature-gated parsing paths. +• Added an explicit `context` parameter to + wil_details_FeatureReporting_RecordUsageInCache and propagate the new + prototype to all call sites, so stack-based structures are no longer + referenced after scope exit. +• Minor clean-up of related WIL feature-state helpers to remove + redundant branches and always set a valid bit mask before returning. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local user could reliably trigger a use-after-free in +DAS and run arbitrary code as SYSTEM, resulting in full local privilege +escalation. The bug is remotely irrelevant but critical for local +attacks. + +Fix Effectiveness +-------------------------------------------------------------------- +Input length is now verified prior to any dereference, the freed pointer +is no longer accessed after LocalFree, and helper caching no longer +stores stack addresses. The vulnerable code path is therefore +eliminated; exploitation of the original UAF is no longer feasible. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50175_windows.media.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50175_windows.media.dll.txt new file mode 100644 index 0000000..44398b5 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-50175_windows.media.dll.txt @@ -0,0 +1,106 @@ +{'change_count': 160, 'patch_store_uid': '0f2e33f7-3143-4f1b-8865-2b030f23c6e2', 'cve': 'CVE-2025-50175', 'date': 1763403170.819818, 'confidence': 0.19, 'file': 'windows.media.dll', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-50175 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +windows.media.dll – Digital-Media stack (Media Foundation helper +templates handling asynchronous MediaSource open operations and MF +work-queue scheduling). + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Media Foundation helper template + MFPutWorkItem< _lambda_ad0ac243… > +creates a TMFWorkItem object that captures several COM interfaces that +must out-live the asynchronous callback executed later on a Media +Foundation work-queue (queue id 5). + +Prior to the patch the lambda constructor responsible for copying the +captures was emitted by a WRL template instantiation that: + • copied the raw pointer from the caller-supplied ComPtr into the new + work-item instance, but + • never performed an InternalAddRef on the weak-reference parameter + that represents the object state returned to the caller, and + • in error paths relied on the local ComPtr variable instead of the + newly allocated work-item to hold a reference. + +As a consequence the reference count of the captured object(s) could +reach zero as soon as the calling thread released its last reference. +When the MF work-queue later executed the callback the internal +pointers (for example IMFTimedTextTrack or IRandomAccessStreamWith­ +ContentType) were already freed, leading to a classic use-after-free +in privileged code. + +Key structures / fields that were affected: + • this->spThis (MediaSource) – strong ref needed + • this->spOp (IAsyncOperation<…>) " + • this->spSourceResolver (IMFMediaEngineSourceResolver) – " + • this->spState (IUnknown) – " + • <weakRef> (IMFTimedTextTrack) – missing AddRef + +Because the work-item executes inside a high-integrity Media +Foundation process, successful exploitation lets a low-privileged +attacker run arbitrary code in that security context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Patched constructor (cuts down to the essential lines) +this->spThis = (MediaSource*) <spThis>->ptr_; +InternalAddRef(&this->spThis); +this->spOp.ptr_ = *v7; // now AddRef’ed +InternalAddRef(&this->spOp); +... +<weakRef>->ptr_ = (IMFTimedTextTrack*)*a2; // NEW +InternalAddRef(<weakRef>); // NEW – prevents UAF +``` +Before the patch the assignment to <weakRef> existed without the +InternalAddRef, leaving the pointed object unprotected. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User code calls MediaSource::StartAsyncOpenOp(). +2. StartAsyncOpenOp instantiates the problematic lambda and queues it + via MFPutWorkItem2(queue 5). +3. Caller releases the MediaSource / related state quickly. +4. Reference count drops to zero because the lambda failed to AddRef. +5. MF work-queue later runs the callback and dereferences the freed + object – memory corruption / UAF. + +Attack Vector +-------------------------------------------------------------------- +Any local, sandboxed or low-integrity code that can load +windows.media.dll and invoke Media Foundation APIs can trigger the +bug. No user interaction is required beyond opening a crafted media +source that is closed immediately after queuing the operation. + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the template-generated helpers with hand-written +constructors that explicitly call + Microsoft::WRL::ComPtr::<type>::InternalAddRef() +for every captured interface, including the previously unprotected weak +reference. Error logging was also consolidated by adding a new +CallStackTracing::InitInstance helper but this is cosmetic. + +Security Impact +-------------------------------------------------------------------- +The use-after-free occurs in a high-integrity MF worker thread. +Attacker-controlled memory contents can be dereferenced, enabling local +privilege escalation (EoP) or code execution in the Media Foundation +host process. The CVSS impact is therefore Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code takes an explicit strong reference for every captured +interface, guaranteeing that their lifetime extends until the work +item finishes and releases them. No paths remain where the object can +be freed early, so the specific UAF is eliminated. No obvious new +issues were introduced; effectiveness appears solid. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53139_winbio.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53139_winbio.dll.txt new file mode 100644 index 0000000..af2af5e --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53139_winbio.dll.txt @@ -0,0 +1,115 @@ +{'date': 1763407727.5264385, 'confidence': 0.26, 'kb': 'KB5066835', 'patch_store_uid': '2fe1d4ac-1727-4c13-9443-7570b45992bf', 'file': 'winbio.dll', 'change_count': 8, 'cve': 'CVE-2025-53139'} +-------------------------------------------------------------------- +CVE-2025-53139 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hello / winbio.dll (biometric service common & policy code) + +Vulnerability Class +-------------------------------------------------------------------- +Logic-error / type-confusion leading to security-feature bypass + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The helper winbio::GetRegKeyPathFromRegKeyName() is supposed to + translate an enum value (RegKeyName) into the corresponding + registry-value name. + +2. In the vulnerable build the routine completely ignored the enum + that it received; if the caller supplied a non-NULL output pointer + the function unconditionally returned the constant string + "ESSCapableOnLastStart" and reported success (0). + +3. The most important in-tree caller is + winbio::GetBioRegKeyValue(_WINBIO_IDENTITY *,RegKeyName,ULONG *). + Prior to the patch the first parameter (a1 – a _WINBIO_IDENTITY + pointer) was accidentally forwarded to + GetRegKeyPathFromRegKeyName() *in place of* the enum parameter. In + other words, an arbitrary pointer value was interpreted as + RegKeyName while the real enum (a2) was ignored. + +4. Because GetRegKeyPathFromRegKeyName() always returned the same + literal the fault was masked at run-time – the wrong parameter did + not trigger an immediate error, but every request for *any* of the + defined registry values was redirected to the single value + ESSCapableOnLastStart. + +5. Higher-level logic, e.g. WinBioIsESSCapable(), used the helper to + decide whether Enhanced Sign-in Security (ESS) is available. An + attacker able to set the publicly writable HKLM registry value + ESSCapableOnLastStart could therefore make WinBioIsESSCapable() + return an arbitrary result and bypass the ESS enforcement path. + +6. Because the registry data are neither encrypted nor integrity + protected the flaw effectively results in clear-text transmission + of a security decision between components and allows a local + attacker to spoof that decision. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// winbio::GetRegKeyPathFromRegKeyName – vulnerable version +if (a2) { // only checks pointer, ignores a1 + *a2 = L"ESSCapableOnLastStart"; // always returned + return 0; // success for every call +} +... +``` +```c +// winbio::GetBioRegKeyValue – vulnerable call site +lpValue = &unk_18002BB24; +RegKeyPathFromRegKeyName = + winbio::GetRegKeyPathFromRegKeyName(a1, &lpValue); +// a1 is _WINBIO_IDENTITY*, not an enum! +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +WinBioIsESSCapable() + -> winbio::GetBioRegKeyValue(identity, RegKeyName::ESSCapable, &out) + -> GetRegKeyPathFromRegKeyName(identity_ptr, &str) // wrong arg + -> returns constant "ESSCapableOnLastStart" + -> caller reads registry HKLM...\ESSCapableOnLastStart + instead of the intended value and trusts the result. + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to write to HKLM biometric policy +values (e.g. via mis-configured ACL or running with elevated rights) +sets ESSCapableOnLastStart to 1 (or 0) and forces Windows Hello to +believe the system *is* (or is not) ESS-capable, bypassing hardware +or VBS checks and weakening sign-in security. + +Patch Description +-------------------------------------------------------------------- +1. GetRegKeyPathFromRegKeyName() was rewritten: + • parameter is now an int (enum) and is validated. + • explicit switch-like mapping to six legal strings. + • returns ERROR_INVALID_PARAMETER (0x80070057) on out-of-range. +2. All callers were updated – notably GetBioRegKeyValue() now passes + the correct enum (a2) instead of the identity pointer. +3. WinBioIsESSCapable() was simplified to rely solely on the fixed + helper; dead TPM/VBS probing code was removed. +4. Numerous ancillary routines were updated to use the new helper and + corrected string constants (&unk_18002BEE4), plus safe handle / + unique_any wrappers for registry keys. + +Security Impact +-------------------------------------------------------------------- +Before the fix any local user able to modify an unprotected registry +value could spoof the ESS capability flag and bypass Windows Hello’s +Enhanced Sign-in Security requirement. Because the decision was +transported in clear text (registry) and the code silently accepted a +bogus pointer as enum, the bypass required no code-execution inside +winbio.dll. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched binaries validate the enum, refuse invalid inputs, and use +correct registry value names. WinBioIsESSCapable() now consumes the +expected value only, so spoofing another key no longer affects the ESS +logic. The fix fully addresses the identified root cause with minimal +performance impact. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53150_windows.media.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53150_windows.media.dll.txt new file mode 100644 index 0000000..ddb96f7 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53150_windows.media.dll.txt @@ -0,0 +1,125 @@ +{'confidence': 0.27, 'cve': 'CVE-2025-53150', 'file': 'windows.media.dll', 'kb': 'KB5066835', 'patch_store_uid': '0f2e33f7-3143-4f1b-8865-2b030f23c6e2', 'change_count': 160, 'date': 1763406141.4376204} +-------------------------------------------------------------------- +CVE-2025-53150 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – Media Foundation / windows.media.dll +( Adaptive Streaming – CMFSourceBufferManager class ) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free +CWE-362: Race Condition / Missing Synchronisation + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CMFSourceBufferManager owns one or two cached pointers to the current +IMFAdaptiveMediaStreamVariant object ( members + • m_dwReferencePointEpoch + • m_fInsideBufferFillingTask ) +These members keep only a *raw* interface pointer. + +The public helper + GetCurrentStreamVariant(IMFAdaptiveMediaStreamVariant **ppVar) +returned one of those raw pointers directly: + * no AddRef was taken + * no lock was held while the field was read + +While one thread was retrieving the pointer, another thread could +concurrently enter code paths such as + • TrySetTimeStampOffsetForDiscontinuity() + • NotifyMainSourceBufferFlushed() +that overwrite/Release() the same member fields. This left the caller +with a dangling IMFAdaptiveMediaStreamVariant*. Subsequent vtable +access by the caller dereferenced freed memory – classic UAF. Because +Media Foundation runs inside privileged service processes (e.g. +BroadcastDVRServer, AudioSvc), a carefully-crafted media stream could +co-ordinate the race and pivot the UAF into an elevation of privilege. + +The absence of reference counting and critical-section protection is +the root cause; the race window starts immediately after the raw +pointer is read and lasts until the caller either AddRefs it (never +happened) or is finished using it (undefined lifetime). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before (pseudo, decompiled) +*ppStreamVariant = this->m_dwReferencePointEpoch ? + (IMFAdaptiveMediaStreamVariant*)this->m_dwReferencePointEpoch : + (IMFAdaptiveMediaStreamVariant*)this->m_fInsideBufferFillingTask; +// no AddRef, no lock +return S_OK; +``` +```c +// after +EnterCriticalSection(&this->m_xOnLowMemoryRetryTimer); +*ppStreamVariant = nullptr; +... +if (this->m_dwReferencePointEpoch) +{ + InternalAddRef(&(this->m_dwReferencePointEpoch)); + *ppStreamVariant = (IMFAdaptiveMediaStreamVariant*)this->m_dwReferencePointEpoch; +} +... +LeaveCriticalSection(...); +return hr; +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User data triggers asynchronous playback changes. +2. Thread A calls GetCurrentStreamVariant() and receives non-AddRef’d + pointer. +3. Thread B flushes or seeks (NotifyMainSourceBufferFlushed / + TrySetTimeStampOffsetForDiscontinuity) and releases the variant. +4. Thread A continues to use freed object – UAF. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker supplies specially crafted adaptive streaming media and +controls timing (seek, flush, buffer-switch) to win the race. The +attacker needs code execution in an unprivileged media client process +but can escalate to the service account hosting the MF pipeline. + + +Patch Description +-------------------------------------------------------------------- +• Entirely replaced GetCurrentStreamVariant: + – Acquires m_xOnLowMemoryRetryTimer critical section. + – Always zero-initialises *ppStreamVariant. + – Takes an InternalAddRef on whichever cached pointer is returned. + – Releases the lock before returning. + +• Related functions augmented to use the same lock and to reset + m_ullWaitForContinuityKey to eliminate stale pointers. + +• Extensive WIL feature gates added but the core safety fix is the + AddRef + critical-section pair. + + +Security Impact +-------------------------------------------------------------------- +Before the patch an authenticated local attacker could cause media +foundation code to dereference freed memory, achieving arbitrary code +execution inside a higher-privilege process, leading to elevation of +privilege (EoP). Memory corruption is process-wide; system +compromise is possible. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation closes the race window by: + 1. Serialising access with a critical section. + 2. Incrementing the COM reference count before returning the + pointer, guaranteeing object lifetime to the caller. +No residual path returns an unreferenced pointer, therefore the UAF +condition is removed. Patch is judged effective provided all callers +respect COM reference conventions. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53717_vmcompute.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53717_vmcompute.exe.txt new file mode 100644 index 0000000..b2d1f33 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53717_vmcompute.exe.txt @@ -0,0 +1,124 @@ +{'confidence': 0.27, 'kb': 'KB5066835', 'change_count': 11, 'file': 'vmcompute.exe', 'patch_store_uid': '07663ad0-52a2-4018-be0d-63fb1362ba50', 'cve': 'CVE-2025-53717', 'date': 1763403084.6653953} +-------------------------------------------------------------------- +CVE-2025-53717 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +vmcompute.exe (host-side Hyper-V management service) +Functions that participate in virtual NUMA bookkeeping and in the +GlobalMemoryBalancer: + • DmVirtualNumaNode::UpdateAssignedVpCount + • GlobalMemoryBalancer::ResourcepGetMaxLpRatio + • GlobalMemoryBalancer::ResourcepGetBestFit + • GlobalMemoryBalancer::ResourceAssignVirtualToPhysical + +Vulnerability Class +-------------------------------------------------------------------- +Logic flaw / reliance on untrusted input in a security decision +(CWE-807) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The host process keeps per-physical-node statistics in the structure + GlobalMemoryBalancer::Resource + +32h AssignedLpCount + +36h AssignedVpCount + +When a guest NUMA node is mapped, UpdateAssignedVpCount() is supposed +to increment one of those counters, depending on whether NUMA spanning +(guest node covering several physical nodes) is enabled. The +vulnerable implementation always updated *both* fields and, more +importantly, wrote the update into whatever element operator[] happened +to return (the decompiled call passed no explicit index). + +Because the guest completely controls the spanning mask that reaches +ResourceAssignVirtualToPhysical(), an attacker can repeatedly assign +the same virtual processors to several nodes. Owing to the accounting +bug, AssignedVpCount stays artificially low, and the later admission +checks in ResourcepGetMaxLpRatio() / ResourcepGetBestFit() treat the +physical NUMA node as lightly loaded. The GlobalMemoryBalancer then +selects the node even though its LP-per-VP limit has already been +exceeded, allowing creation of additional VBS enclaves that run with +host-level virtual-processor privileges. + +New code introduces a feature gate called + Feature_NumaSpanningBugFix +and changes every affected routine so that, when the gate is enabled: + • UpdateAssignedVpCount() only modifies AssignedLpCount (+32h) for + the spanning case and uses a correct span index (second argument + now carries the index). + • Consumers choose the correct counter (+32h or +36h) depending on + the gate, eliminating the under-count. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before: wrong index, unconditional VP accounting +if (*(BYTE *)(a1+351) < *a2) { + p = span<Resource>::operator[](a2); // always first element + *(DWORD *)(p+32) += *(DWORD *)(a1+344); + if (*(DWORD *)(a1+344)) + *(DWORD *)(p+36) += *(DWORD *)(*(QWORD *)(a1+536)+32) * + *(DWORD *)(a1+344); +} + +// After: index passed, behaviour gated +v5 = span<Resource>::operator[](a2, v4); +if (FeatureEnabled()) { + if (v6) + *(DWORD *)(v5+32) += *(DWORD *)(ptr+32) * v6; +} else { + *(DWORD *)(v5+32) += v6; + if (v7) + *(DWORD *)(v5+36) += *(DWORD *)(ptr+32) * v7; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Guest/administrator enables NUMA spanning and supplies a crafted + virtual-to-physical node mask. +2. Host vmcompute.exe enters + ResourceAssignVirtualToPhysical() -> UpdateAssignedVpCount(). +3. Because of the bug, AssignedVpCount of the targeted physical node + is *not* incremented. +4. Subsequent placement helpers (ResourcepGetMaxLpRatio and + ResourcepGetBestFit) rely on the under-reported counters and accept + additional mappings. +5. VBS enclave starts on an over-committed node, giving enclave code + access to host-level VP privileges (local EoP). + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to create or reconfigure a VM on the +host (including inside a guest that runs in nested-virtualisation +scenarios) sets NUMA spanning and repeatedly re-assigns the same VPs. +No additional privileges are required beyond VM management rights. + +Patch Description +-------------------------------------------------------------------- +1. Added feature flag Feature_NumaSpanningBugFix. +2. UpdateAssignedVpCount() now receives the span index and updates the + correct counter depending on the flag. +3. Every consumer routine selects between AssignedLpCount (+32h) and + AssignedVpCount (+36h) under the same flag. +4. ResourceAssignVirtualToPhysical() rewritten to pass the correct + index and use the gated accounting. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, over-commit checks could be bypassed, letting an +attacker create VBS enclaves with more virtual processors than +permitted. Because the enclave executes in VTL1, this breaks the +intended privilege boundary and allows local elevation to the +virtual-processor context of the host. + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected logic removes the mis-count and uses the span index that +matches the physical resource being updated. Assuming the Windows +feature flag is enabled system-wide, the vulnerability is fully +neutralised. If the flag is disabled by policy, the original flaw +would persist; therefore deployment guidance must ensure the feature is +turned on everywhere. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53717_vmwp.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53717_vmwp.exe.txt new file mode 100644 index 0000000..72c6d23 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53717_vmwp.exe.txt @@ -0,0 +1,123 @@ +{'cve': 'CVE-2025-53717', 'file': 'vmwp.exe', 'date': 1763403065.4230146, 'patch_store_uid': '4de479d1-4f75-495b-8dfb-39c940b3fadc', 'change_count': 4, 'kb': 'KB5066835', 'confidence': 0.1} +-------------------------------------------------------------------- +CVE-2025-53717 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Hyper-V worker process (vmwp.exe) – code that restores a saved VM and +that evaluates WIL (Windows-in-box Limiter) feature flags. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-807 Reliance on Untrusted Inputs in a Security Decision. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Two independent code paths used attacker-controllable data when +building a security decision. + +1. wil::details::FeatureImpl::<TestLabVal/TestGateImp>:: + GetCurrentFeatureEnabledState() + • The function receives an opaque FEATURE_ENABLED_STATE value that + can be influenced through registry and policy overrides. + • Before the patch the code derived a 32-bit result mask (bits + encode Enabled / Disabled / Variant, Mandatory-Telemetry etc.). + Bit 0 (“state valid”) was cleared again if the combination of + bits 0x40/0x400/0x800 did not match a set of hard-coded + predicates (v10/v11 logic). + • An attacker could therefore arrange for the mask to indicate + “feature enabled” (bit 0x40) while simultaneously clearing the + “state valid” flag (bit 0). Down-stream consumers treated + “!bit0” as “trust the caller”, effectively bypassing the normal + enforcement path. + • Patch effect: the whole predicate block was deleted and the + result is now built as *mask = (flags | 1); i.e. bit 0 is set + unconditionally and can no longer be suppressed by the caller. + +2. WorkerTaskStarting::RunRestoreStartSteps() + • During restore the worker decides whether it is allowed to write + an optimisation buffer that bypasses zero-initialisation of + secret RAM pages (variable v57 before patch, v58 after). + • The old decision was based solely on the registry value + HKLM\…\EnableAllZeroBufferWrite. Any local admin – or code + running in a VBS enclave – could toggle that value and force the + worker to **skip** the zero-fill, leaving sensitive data in + cleartext inside the VM RAM image. + • The patch adds a second gate: the operation is now permitted only + if BOTH the registry key is set and an internal + wil::Feature_Servicing_MktmeVmRestoreCrash is enabled *and* the + underlying hardware exposes a safe capability via a VID call. + This removes the sole reliance on the untrusted registry input. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before – bit0 could be cleared again +v11 = 1; +if ( (*mask & 0x40) == 0 || !v10 ) + v11 = 0; +*mask = v11 | *mask & 0xFFFFFFFE; // may clear bit0 + +// After – bit0 forced to 1 +*mask = v7 | v8 | 1; // cannot be cleared by caller +``` +```c +// Before – single-factor decision +if (!QueryValue(...,"EnableAllZeroBufferWrite", v46) || v46[0] != 1) + v57 = 1; // allow optimisation + +// After – additional feature + HW check +if ((queryFailedOrNot1) && + (!Feature_IsEnabled(...Servicing_MktmeVmRestoreCrash) || + !PartitionHwCap())) + v58 = 1; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged attacker modifies HKLM key or WIL policy in the host. +2. Hyper-V manager (vmwp.exe) starts VM restore. +3. GetCurrentFeatureEnabledState builds a malformed mask with bit 0 + cleared while keeping enable bits set. +4. WorkerTaskStarting consumes this mask / registry flag and enables + the fast RAM path. +5. Secret pages are restored without mandatory zeroing, allowing local + read-out from the saved-state file and privilege escalation inside a + VBS enclave. + +Attack Vector +-------------------------------------------------------------------- +Local – attacker needs the ability to modify virtualisation registry +keys or WIL per-feature overrides. No administrator rights inside the +host kernel are required; the exploit is carried out from user mode +inside a VBS enclave process running with normal user privileges. + +Patch Description +-------------------------------------------------------------------- +1. Simplified feature-state construction – removed complex secondary + predicate and forces STATE_VALID (bit0) to 1. +2. Removed unused __private_IsEnabled branch and redundant variable + juggling. +3. Hardened RestoreStartSteps: now requires *both* a feature gate and + a hardware capability in addition to the registry toggle before the + optimisation is used. +4. Minor clean-ups: variable renaming, dead-code removal, consistent + initialisation. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could make Hyper-V restore a VM with +unencrypted or non-zeroed memory pages. Those pages can contain host +physical addresses mapped into the VBS enclave, allowing execution of +arbitrary code in the worker process context and ultimately SYSTEM- +level privilege escalation (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the possibility to fabricate an "enabled yet +untrusted" feature mask and no longer makes a security decision based +solely on a registry key. Because both conditions are now +simultaneously required and bit 0 is enforced, the previously reachable +bypass state cannot be reproduced. No residual paths that still rely +on the untrusted inputs were observed in the updated diff. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53768_storsvc.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53768_storsvc.dll.txt new file mode 100644 index 0000000..d192ca6 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-53768_storsvc.dll.txt @@ -0,0 +1,118 @@ +{'date': 1763407737.4985454, 'file': 'storsvc.dll', 'patch_store_uid': 'a1741a28-bbc5-430a-8ba9-00654414c2cd', 'kb': 'KB5066835', 'confidence': 0.12, 'cve': 'CVE-2025-53768', 'change_count': 11} +-------------------------------------------------------------------- +CVE-2025-53768 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows storsvc.dll (Storage Service), Xbox edition – routine +ScValidateProvisioning() that validates an attacker-supplied +SP_PROVISIONING_INFO structure received by the IStorageService IPC +interface. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free assisted by missing bounds checking +CWE-362: Race Condition (concurrent access to shared service state) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ScValidateProvisioning() performs extensive sanity checks on the caller +supplied SP_PROVISIONING_INFO before the structure is copied into the +service’s global provisioning context. One of the fields checked is +DWORD 6 (zero-based) – the maximum allowed element count for a later +array allocation. Prior to the patch that field was only validated +when function Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() +returned non-zero. On several console SKUs a second, OEM-flavoured +feature switch named Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_0() +is the one that is actually enabled. Consequently, on affected +systems the bounds test for DWORD 6 was silently skipped and arbitrary +attacker supplied values were accepted. + +The unchecked value is propagated to later allocation logic inside the +service worker thread. During normal provisioning the worker allocates +an array whose size equals DWORD 6, performs work, then frees the +buffer in a second thread. Because the service trusts the unchecked +field any caller can request a zero-byte allocation, later causing the +producer thread to write at negative offset 1 when it updates the +terminating element. A concurrent consumer may already have freed or +re-allocated the same heap block, turning the out-of-bounds store into +an intra-heap use-after-free that yields controlled corruption of a +service-process pointer. + +Parameters/structures of interest: + struct _SP_PROVISIONING_INFO + +0 DWORD Version + +4 DWORD Flags + +8 QWORD Alignment + +20 DWORD MaxSecureVolumes (field 5) + +24 DWORD ElementCount (field 6, unchecked) + +28 DWORD VolumeLimit (field 7) + +The race aspect (CWE-362) appears because the worker thread and the +IPC handler share the same _SP_PROVISIONING_INFO instance without +locking; the attacker can provoke re-allocation while writes are in +progress, widening the abuse window. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old logic – DWORD 6 validated only when the single feature flag is on +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + v8 = *((_DWORD *)a1 + 6); // ElementCount + if (v8) { + if (v8 - 1 > 4 || *((_DWORD *)a1 + 5) > v8 || + v8 >= *((_DWORD *)a1 + 7)) + return STATUS_INVALID_PARAMETER; + } +} + +// patched logic – either feature flag activates the bounds check +if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_0() || + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage()) { + v8 = *((_DWORD *)a1 + 6); + ... identical range validation ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker connects to IStorageService and calls the provisioning + endpoint, supplying crafted SP_PROVISIONING_INFO. +2. ScValidateProvisioning() is executed inside storsvc.dll. +3. Because the console only has feature flag _0 set, the range check on + ElementCount is skipped. +4. ElementCount is copied into global context. +5. Worker thread allocates a 0-byte (or abnormally small) buffer and + later writes past its bounds, accessing freed memory. +6. Memory corruption leads to elevation of privilege within the + service process. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user or sandboxed game title able to talk to the +storage service over ALPC / DevKit RPC. No admin rights needed. + +Patch Description +-------------------------------------------------------------------- +The fix broadens the gating condition so that the ElementCount bounds +check is executed when either of two feature switches is enabled: +Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_0() OR +Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage(). This guarantees +that all supported console SKUs perform the necessary validation and +reject out-of-range ElementCount values before the structure is used. +No other code changes were observed. + +Security Impact +-------------------------------------------------------------------- +On vulnerable builds an attacker can obtain code execution in the +Storage Service (Medium IL / service context) from a low-privileged +application, thereby achieving local elevation of privilege. The issue +can be chained with other Xbox kernel bugs to gain full system control. + +Fix Effectiveness +-------------------------------------------------------------------- +The additional ORed feature check forces the validation branch to run +on all device configurations. Provided that the existing range test is +correct, out-of-range ElementCount values are now blocked and the +use-after-free cannot be triggered. No bypass is known. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55326_cdpsvc.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55326_cdpsvc.dll.txt new file mode 100644 index 0000000..4a4b524 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55326_cdpsvc.dll.txt @@ -0,0 +1,108 @@ +{'kb': 'KB5066835', 'change_count': 8, 'confidence': 0.46, 'patch_store_uid': '0e54175f-9f22-44cd-8cdd-4e0fede69033', 'cve': 'CVE-2025-55326', 'date': 1763402971.2739463, 'file': 'cdpsvc.dll'} +-------------------------------------------------------------------- +CVE-2025-55326 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (cdpsvc.dll) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine is + CDPComDevice::DeviceCallback::OnAppTargetListReceived(). + +1. The original implementation builds a lambda functor on the stack + (variable v6) using the template type + std::_Func_impl_no_alloc<lambda,…>. +2. The lambda captures the *addresses* of three local automatic + variables (v11, v12, v13) that hold the pointers supplied by the + caller: + • const ICDPAppId *a2 -> v11 + • const char **a3 -> v12 + • unsigned short a4 -> v13 +3. A pointer to that on-stack functor is passed to + ServiceCallbackNotifier<…>::StoreResult() + which persists the std::function object for asynchronous execution. +4. On return from OnAppTargetListReceived the stack frame is destroyed. + The notifier therefore keeps dangling pointers to freed memory. +5. When the asynchronous callback later executes, it dereferences the + invalid captures, resulting in a classic use-after-free. Because + cdpsvc runs inside a service process, an attacker can control the + freed data contents and achieve arbitrary code execution in the + service context. + +No heap cloning or reference counting existed; lifetime of the functor +was incorrectly assumed to match the notifier lifetime. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (simplified) +v13 = a4; // local +v12 = a3; // local +v11 = a2; // local +*(_QWORD*)&v5 = &v11; // capture ADDRESS of v11 +*((_QWORD*)&v5 + 1) = &v13; // capture ADDRESS of v13 +v6 = &std::_Func_impl_no_alloc<lambda,…>::`vftable'; // functor on stack +ServiceCallbackNotifier::StoreResult(this+32); // stores pointer to v6 +// v6 and captured locals destroyed on return -> dangling pointer +``` + +```c +// after patch (simplified) +char functor[24]; // v6 +char funcObj[72]; // v7 (std::function storage) +make_lambda(functor,&v9,&v11,&v10); // build functor with VALUES +std::function_ctor(funcObj,functor); // copies functor into object +ServiceCallbackNotifier::StoreResult(this+32); +// StoreResult receives an owning copy, independent of stack +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Network packet -> CDP framework -> Device layer -> +CDPComDevice::DeviceCallback::OnAppTargetListReceived() -> +ServiceCallbackNotifier stores dangling lambda -> asynchronous callback +executes after stack is gone -> UAF + +Attack Vector +-------------------------------------------------------------------- +A remote, unauthenticated attacker sends crafted Connected Devices +Platform (CDP) traffic that causes cdpsvc to deliver an AppTargetList to +an affected client, thereby invoking OnAppTargetListReceived. When the +service later processes the stored callback, it dereferences freed +memory, allowing the attacker to corrupt the heap and execute arbitrary +code within the cdpsvc service process. + +Patch Description +-------------------------------------------------------------------- +• Re-implemented lambda construction so that captured *values* are + copied, not addresses of stack locals. +• The functor is first built in a temporary buffer (v6) and then moved + into a std::function object (v7) whose storage is either inlined or + heap-allocated and owned by ServiceCallbackNotifier. +• All later accesses therefore refer to valid memory with the correct + lifetime. +(Additional unrelated clean-ups in FeatureImpl functions were applied but +are not relevant to the UAF fix.) + +Security Impact +-------------------------------------------------------------------- +Successful exploitation yields arbitrary code execution in the cdpsvc +service, enabling remote code execution with LocalService privileges and +potential privilege escalation depending on subsequent chain. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code eliminates dangling captures by ensuring the callback +owns a self-contained copy of the functor. No stack addresses are +persisted, preventing the original lifetime mismatch. No residual paths +holding stack references were observed in the diff, so the fix appears +complete for this call site. Effectiveness for other cdpsvc callbacks +is unknown. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55328_vmcompute.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55328_vmcompute.exe.txt new file mode 100644 index 0000000..b1db90c --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55328_vmcompute.exe.txt @@ -0,0 +1,121 @@ +{'patch_store_uid': '07663ad0-52a2-4018-be0d-63fb1362ba50', 'kb': 'KB5066835', 'date': 1763407747.4307406, 'change_count': 11, 'confidence': 0.14, 'cve': 'CVE-2025-55328', 'file': 'vmcompute.exe'} +-------------------------------------------------------------------- +CVE-2025-55328 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V / vmcompute.exe – Virtual NUMA and memory-balancer +code that maps virtual processors (VP) to physical logical +processors (LP) and keeps per-node accounting in +GlobalMemoryBalancer::Resource[Info] structures. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Concurrent execution using shared resource with improper +synchronisation (race condition). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper routines update shared +GlobalMemoryBalancer::Resource objects that are stored in a gsl::span +array and are accessed by multiple worker threads while creating or +re-configuring a virtual machine. + +1. DmVirtualNumaNode::UpdateAssignedVpCount() + • Before the fix the code called + gsl::span<...>::operator[](a2) without supplying the index of the + NUMA node that is being processed. All callers therefore wrote + into entry #0 of the array, irrespective of the NUMA node + ( *(DWORD)(result+32/36) += ... ). + • No locking or atomics were used; concurrent calls from different + nodes raced on the same cache line and produced lost updates or + out-of-range counts. + +2. GlobalMemoryBalancer::ResourcepGetMaxLpRatio(), + ResourcepGetBestFit(), and ResourceAssignVirtualToPhysical() used + the same pattern: operator[] was invoked without an explicit index + and fields at offset 32 / 36 were modified unconditionally. The + patch now calls operator[](span, index) and updates only one of the + two fields (32 or 36) depending on a new feature flag + Feature_NumaSpanningBugFix. + +Because of the missing index and the absence of synchronisation, two +threads that configure different Virtual NUMA nodes can add the same +VPs to the same Resource structure, causing incorrect accounting. +Once the counters wrap or become negative, later arithmetic (e.g. +ratio calculation) divides by the wrong value and may select a +Resource that should have been considered exhausted. That allows a +privileged guest or management application that controls VM creation +(tools running inside the partition) to over-allocate LPs, breach the +isolation guarantees and execute with elevated privileges in the host +partition. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +result = gsl::span<...>::operator[](a2); // no index +*(_DWORD *)(result + 32) += *(_DWORD *)(a1+344); +... + +// after +v5 = gsl::span<...>::operator[](a2, v4); // pass index +if ( Feature_NumaSpanningBugFix ) { + if ( v6 ) + *(_DWORD *)(v5 + 32) += *(DWORD *)(a2+32) * v6; +} else { + *(_DWORD *)(v5 + 32) += v6; + *(_DWORD *)(v5 + 36) += *(DWORD *)(a2+32) * v6; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. A management API (HCS / WMI / PowerShell) creates or resizes a VM + with NUMA spanning enabled. +2. vmcompute.exe calls + GlobalMemoryBalancer::ResourceAssignVirtualToPhysical(), which + iterates over every VP. +3. Each VP invokes DmVirtualNumaNode::UpdateAssignedVpCount(). +4. Because the shared span entry is selected incorrectly, all threads + write into the same Resource object at the same time. +5. The resulting corrupted counters allow an attacker-controlled VM to + obtain more LPs than authorised and to execute code with host + privileges. + +Attack Vector +-------------------------------------------------------------------- +Local attacker who can create or modify a Hyper-V virtual machine (for +example, a low-privileged administrator inside a guest or a tenant in +multi-tenant hosting) races multiple configuration requests that span +across NUMA nodes to trigger the counter corruption. + +Patch Description +-------------------------------------------------------------------- +• All affected functions now call gsl::span::operator[](span, index), + ensuring that each NUMA node updates its own Resource entry. +• Updates of fields at offset 32 and 36 are now mutually exclusive and + gated by the new runtime flag + Feature_NumaSpanningBugFix (wil-feature id 2578215227). +• The rest of the edits (signature changes in + wil::details::ReportUsageToService and its callers) are telemetry + only and do not affect the vulnerability. + +Security Impact +-------------------------------------------------------------------- +Before the fix concurrent configuration of NUMA-spanning VMs could +corrupt shared accounting data, allowing over-allocation of logical +processors and breaking isolation barriers between guest and host. +An attacker with VM-management privileges could leverage the race to +run code with elevated (host) privileges – an elevation-of-privilege +(EoP) scenario. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch eliminates the data race by writing to the correct Resource +slot and by avoiding duplicate counter increments. No additional +locking was introduced; correctness now relies on each thread using +its own slot, which removes the previously shared write path. If all +callers pass the proper index this fully mitigates the identified +issue; residual risk is considered low. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55330_fvevol.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55330_fvevol.sys.txt new file mode 100644 index 0000000..49141a2 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55330_fvevol.sys.txt @@ -0,0 +1,129 @@ +{'kb': 'KB5066835', 'date': 1763407735.4787972, 'confidence': 0.23, 'change_count': 6, 'patch_store_uid': 'efb29de7-ce28-4d45-a691-32e878d118b2', 'file': 'fvevol.sys', 'cve': 'CVE-2025-55330'} +-------------------------------------------------------------------- +CVE-2025-55330 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows BitLocker filter driver (fvevol.sys). +Vulnerable routine: FveFilterDeviceControl. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper enforcement of behavioral workflow / reference-count misuse +(CWE-841). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The BitLocker filter exports two private IOCTLs used by upper‐level +BitLocker components: + • 0x455610D8 – FVE_RW_DEVICE_INIT (locks the driver) + • 0x455610DC – FVE_RW_DEVICE_CLEANUP (unlocks the driver) + +On INIT the driver increments a 32-bit reference counter located at +(DeviceCtx + 0x11B4) – the decompiler shows this as + _InterlockedIncrement((int *)v2 + 1133) +followed by a call to FveLockDriver(). + +On CLEANUP the counter is decremented and FveUnlockDriver() is invoked +*unconditionally*: + if (_InterlockedDecrement((int *)v2 + 1133) < 0) + KeBugCheckEx(...); + ... + FveUnlockDriver(v2); + +Two problems arise: +1. Workflow is not validated. A CLEANUP request can be issued when the + counter is already zero (i.e. no preceding INIT), causing the count + to under-flow to ‑1. +2. Even though an under-flow is detected, FveUnlockDriver() is still + reached whenever the fatal call to KeBugCheckEx() is compiled out or + otherwise bypassed (e.g. by certain feature flags or by attacking in + a pre-OS environment where the bugcheck cannot be serviced). + +Because FveUnlockDriver() removes the global volume lock, subsequent +I/O bypasses BitLocker’s encryption path, giving the attacker raw +sector access and therefore a BitLocker security-feature bypass. +Nothing prevents a physical attacker from sending the single IOCTL to a +blocked volume before the OS fully boots. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch: +```c +// case IOCTL_FVE_RW_DEVICE_CLEANUP (0x455610DC) +if (_InterlockedDecrement((int *)v2 + 1133) < 0) + KeBugCheckEx(0x120, 0xC, 0, 0, 0); +... +FveUnlockDriver(v2); // always executed +``` +After patch: +```c +v23 = _InterlockedDecrement((int *)v2 + 1133); +if (Feature_IsEnabled()) { + if (v23 < 0) + WPP_SF_qd(...); // log only +} else if (v23 < 0) { + KeBugCheckEx(...); +} +... +if (!Feature_IsEnabled()) // unlock only for correct workflow + FveUnlockDriver(v2); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens the volume device exposed by fvevol.sys. +2. Sends DeviceIoControl with code 0x455610DC (CLEANUP) without ever + sending the matching 0x455610D8 (INIT). +3. Reference count becomes ‑1. +4. FveUnlockDriver() executes and clears the BitLocker filter lock. +5. Subsequent read/write requests reach the underlying block device in + clear-text. + + +Attack Vector +-------------------------------------------------------------------- +Requires physical access to the machine (e.g. booting from external +media) or any context able to issue raw device-control requests before +BitLocker is fully engaged. No authentication is needed – the IOCTL is +accepted as long as RequestorMode == KernelMode, which is satisfied in +boot or recovery environments under attacker control. + + +Patch Description +-------------------------------------------------------------------- +The fix adds a feature-guarded branch that: +1. Stores the post-decrement value in a temporary variable. +2. When the value is negative, merely logs an ETW event instead of + allowing execution to proceed directly. +3. Crucially, the subsequent call to FveUnlockDriver() is placed behind + the same feature gate. If the guard is active (i.e. fix enabled) + and the workflow is broken, the driver is *not* unlocked. + +Additional non-security changes (trace-GUID updates, signature change of +FveDiscoverVolume, ETW metadata relocation) are incidental. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could issue a single malformed IOCTL to +force FveUnlockDriver() and thereby gain unencrypted access to the disk +contents. This constitutes a BitLocker security-feature bypass with +physical-access threat model. Integrity and confidentiality of the +protected volume are lost. + + +Fix Effectiveness +-------------------------------------------------------------------- +By withholding FveUnlockDriver() when the reference count becomes +negative, the patch closes the workflow gap: an unmatched CLEANUP no +longer removes the BitLocker driver lock. The vulnerability is +therefore effectively mitigated as long as the new feature flag is +present and enabled on all affected systems. + + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55332_bdesvc.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55332_bdesvc.dll.txt new file mode 100644 index 0000000..467bcdf --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55332_bdesvc.dll.txt @@ -0,0 +1,137 @@ +{'patch_store_uid': '1a19a051-6137-4f63-b4c8-3d7ae9d228f0', 'kb': 'KB5066835', 'change_count': 3, 'file': 'bdesvc.dll', 'cve': 'CVE-2025-55332', 'date': 1763403023.313475, 'confidence': 0.13} +-------------------------------------------------------------------- +CVE-2025-55332 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows BitLocker Device-Encryption service (bdesvc.dll) +Functions: + • FveEnableEAS::ProvisionDeviceEncryption() + • FveEnableEAS::BackupRecoveryPasswordWithRetry() + • FveEnableEAS::BackupRecoveryPassword() + +Vulnerability Class +-------------------------------------------------------------------- +Improper Enforcement of Behavioral Workflow (CWE-841) – Security +feature bypass in the BitLocker device-encryption provisioning flow. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When Device Encryption is provisioned the service executes the +following ordered steps (simplified): + 1. Determine the target FVE volume path. + 2. Compute the required provisioning steps (backup password, + create TPM protector, delete clear key, etc.). + 3. Carry out each required step. + 4. Commit the accumulated changes to the FVE volume. + +In the vulnerable build step 4 is performed with a plain call to + + FveCommitChanges(<VolumeHandle>); + +This basic API commits whatever state currently exists on the volume. +If any earlier step (e.g. backup of the recovery password, TPM +protector creation, clear-key deletion) silently failed or was +intentionally interrupted, the commit still succeeds and BitLocker is +left enabled **without the expected protective artefacts**. An +attacker that can influence the environment (for example by blocking +network connectivity so that cloud/AD/MSA backup fails, or by +preventing TPM protector creation) can therefore force Device +Encryption to finish in a mis-configured state – typically leaving a +clear key on disk. With physical access the attacker can then use the +residual clear key to unlock the drive, completely bypassing the data +-at-rest protection BitLocker is supposed to guarantee. + +The patch introduces a guarded call to a newer API: + + if ( Feature_BitLocker_Block_Backup_DeviceEncryption ) + FveCommitChangesEx(<Handle>, 0x0A); + else + FveCommitChanges(<Handle>); + +FveCommitChangesEx takes an explicit flag mask (0x0A) that instructs +the FVE driver to **refuse the commit unless all mandatory backup and +protector requirements are satisfied**. If the feature switch is not +yet enabled the code falls back to the old behaviour, preserving +compatibility. All logging identifiers were also updated, but the +security fix is the change from the unconditional FveCommitChanges() +call to the conditional, policy-aware FveCommitChangesEx(). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v19 = FveCommitChanges(v24); // commits even if backup/protector failed +... + +// after (excerpt) +if (Feature_BitLocker_Block_Backup_DeviceEncryption) +{ + v19 = FveCommitChangesEx(v25, 0x0A); // enforce policy + if (v19 < 0) { /* error path */ } +} +else +{ + v20 = FveCommitChanges(v25); // legacy path +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User or MDM requests Device-Encryption → +2. FveEnableEAS::ProvisionDeviceEncryption() called. +3. Function determines provisioning steps; finds that recovery password + backup and TPM protector creation are needed. +4. Attacker causes the backup step (network, OneDrive, AD, etc.) to + fail or to be skipped. +5. Despite v6 < 0 from failed backup, code path continues, reaches + FveCommitChanges(vHandle) which returns success. +6. Volume is left encrypted but still contains an un-revoked clear key + (or lacks a TPM protector), allowing anyone with physical access to + boot and read data. + +Attack Vector +-------------------------------------------------------------------- +A local attacker with physical access starts Device Encryption during +initial device setup (or forces an enterprise policy refresh) and then +blocks network / cloud connectivity or otherwise sabotages the backup +and protector-creation steps. Because the service commits the changes +unconditionally, encryption is finalised without the required security +artifacts, letting the attacker later obtain the clear key and bypass +BitLocker protection. + +Patch Description +-------------------------------------------------------------------- +1. Introduced a new feature gate + Feature_BitLocker_Block_Backup_DeviceEncryption. +2. Replaced unconditional FveCommitChanges() with + FveCommitChangesEx(handle, 0x0A) when the feature is enabled. The + extended commit API validates that mandatory backup / protector + prerequisites are met before accepting the commit. +3. Added fallback to the original API when the feature flag is not yet + enabled to maintain compatibility. +4. Numerous ancillary changes (renamed WPP trace IDs, variable + initialisation clean-ups, type corrections) that have no security + impact but accompany the main fix. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a malicious actor could cause Device Encryption to +complete without a valid recovery protector or backup, effectively +leaving a clear key on the disk. This renders the encryption +ineffective against offline or stolen-device attacks, constituting a +Security Feature Bypass (CVE-2025-55332). + +Fix Effectiveness +-------------------------------------------------------------------- +Using FveCommitChangesEx with the 0x0A control flags moves the +enforcement into the kernel: if the recovery key is not adequately +protected / backed up the commit fails and the provisioning routine +bails out, preventing the vulnerable end state. Because the new path +is protected by a feature flag, the mitigation is only active when the +BitLocker_Block_Backup_DeviceEncryption feature is enabled; if the flag +is disabled the legacy behaviour remains, but Microsoft can remotely +switch the feature on for all supported systems. Assuming the flag is +enabled, the fix robustly closes the workflow gap and prevents the +bypass. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55332_fvevol.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55332_fvevol.sys.txt new file mode 100644 index 0000000..c2716a9 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55332_fvevol.sys.txt @@ -0,0 +1,117 @@ +{'patch_store_uid': 'efb29de7-ce28-4d45-a691-32e878d118b2', 'change_count': 6, 'file': 'fvevol.sys', 'cve': 'CVE-2025-55332', 'confidence': 0.24, 'kb': 'KB5066835', 'date': 1763403052.4465086} +-------------------------------------------------------------------- +CVE-2025-55332 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft BitLocker filter driver (fvevol.sys) – routine +FveFilterDeviceControl() that processes DeviceIoControl IRPs for the +BitLocker volume-filter device. + +Vulnerability Class +-------------------------------------------------------------------- +Improper enforcement of behavioral workflow / reference counter logic +(CWE-841). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +FveFilterDeviceControl() implements an internal reference counting +scheme that protects the driver against raw read / write operations +while BitLocker is active. Two private IOCTLs are used: + • 0x455610D8 – FveReadWriteDeviceInit (increments counter and + eventually calls FveLockDriver()). + • 0x455610DC – FveReadWriteDeviceCleanup (decrements counter and, when + the count reaches 0, calls FveUnlockDriver()). + +Before the patch the code simply performed + _InterlockedDecrement(&DevCtx->ReadWriteUsers); +then *unconditionally* executed FveReadWriteDeviceCleanup() and +FveUnlockDriver(), the only safety net being a BugCheck when the count +became negative. An attacker able to send the cleanup IOCTL without a +matching init IOCTL could therefore drive the counter below zero. If +crash-handling was suppressed (for example, on systems booted with +"no-reboot" or with special feature flags) the negative value was +accepted and FveUnlockDriver() was still invoked, leaving the volume in +an unlocked state and bypassing BitLocker protections. + +Patch changes: +1. The return value of InterlockedDecrement() is stored (v23). +2. If the new feature flag + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1() is enabled and + the count goes negative, the driver now *only logs* the anomaly; it + no longer calls FveUnlockDriver(). +3. When the flag is *not* enabled the old BugCheck path is preserved, + but FveUnlockDriver() is still skipped if the count is negative. + +Therefore the unlock routine can now only be reached when the reference +count is valid (>=0), eliminating the possibility of releasing the +BitLocker lock through an unbalanced IOCTL sequence. + +Other changes in the diff (ETW / WPP GUID updates, parameter reduction +in FveDiscoverVolume, cosmetic renaming) are bookkeeping and do not +impact the core issue. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +case 0x455610DC: // FVE_RW_DEVICE_CLEANUP + if (!a2->RequestorMode) { + _InterlockedDecrement(&DevCtx->ReadWriteUsers); + if (DevCtx->ReadWriteUsers < 0) + KeBugCheckEx(...); + FveReadWriteDeviceCleanup(DevCtx, 0); + FveUnlockDriver(DevCtx); // executes even if count was wrong + } + +// AFTER +v23 = _InterlockedDecrement(&DevCtx->ReadWriteUsers); +if (FeatureEnabled) { + if (v23 < 0) + WPP_SF_qd(...); // log only +} else if (v23 < 0) { + KeBugCheckEx(...); // legacy behaviour +} +FveReadWriteDeviceCleanup(DevCtx, 0); +if (!FeatureEnabled) // unlock only on valid path + FveUnlockDriver(DevCtx); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens the BitLocker device (\\.\FVEVolume{GUID}). +2. Sends IOCTL 0x455610DC without ever having sent IOCTL 0x455610D8. +3. ReadWriteUsers becomes -1. +4. Pre-patch: driver proceeds to FveUnlockDriver() -> volume unlocked. +5. Post-patch: negative count is detected and unlock is *not* executed. + +Attack Vector +-------------------------------------------------------------------- +Requires physical or local administrator access to issue the proprietary +DeviceIoControl codes (e.g., from WinPE or malicious kernel driver). +No authentication to BitLocker secrets is required – the workflow error +alone is sufficient to drop the driver lock. + +Patch Description +-------------------------------------------------------------------- +• Stores the result of InterlockedDecrement() and validates it before + unlocking. +• Skips FveUnlockDriver() when the reference counter is negative. +• Adds WPP telemetry instead of unconditional BugCheck under a guarded + feature flag. +• Minor re-layout of local variables and ETW provider changes. + +Security Impact +-------------------------------------------------------------------- +A local attacker can bypass BitLocker’s read/write protection layer and +obtain unencrypted access to the protected volume, defeating the +confidentiality guarantee of BitLocker (security feature bypass). + +Fix Effectiveness +-------------------------------------------------------------------- +The added counter check blocks the only path that led to an unintended +call to FveUnlockDriver(), thereby enforcing the intended workflow. No +other obvious paths remain that allow the counter to underflow while +still reaching the unlock routine, so the patch is considered effective +for this specific issue. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55333_fvevol.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55333_fvevol.sys.txt new file mode 100644 index 0000000..d20af7d --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55333_fvevol.sys.txt @@ -0,0 +1,162 @@ +{'confidence': 0.29, 'change_count': 6, 'date': 1763407734.1517072, 'cve': 'CVE-2025-55333', 'patch_store_uid': 'efb29de7-ce28-4d45-a691-32e878d118b2', 'kb': 'KB5066835', 'file': 'fvevol.sys'} +-------------------------------------------------------------------- +CVE-2025-55333 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows BitLocker driver (fvevol.sys) +Functions affected: FveFilterDeviceControl() and +FvePdcActivatorCallback(). These routines implement BitLocker +IOCTL-handling and automatic device-activation logic. + + +Vulnerability Class +-------------------------------------------------------------------- +Logical error – incomplete comparison / missing factors +(CWE-1023). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Device state for a BitLocker-protected volume is stored in a + bit-field located at offset +0x318 (decimal 792) of the device + extension ("State" below). The driver decides whether it is safe + to renew a Platform-Data Collection (PDC) activation or to perform + Read/Write cleanup by evaluating this field. + +2. In the vulnerable build the following test is executed inside + FvePdcActivatorCallback(): + + if ((State & 0x100) == 0 || + (Path = *(QWORD *)(Ext+0x3D8), // +984 + Path) && + *(WORD *)Path && + *(WORD *)(Path+2) >= 0x10 && + (*(BYTE *)(Path+8) & 0xC0) != 0x40) + + The test is meant to guarantee that *both* of the following are + true before calling Pdcv2ActivationClientRenewActivation(): + a) The device is in the REQUIRED-ACTIVATION state (bit 0x100). + b) The secondary path buffer at +0x3D8 is valid and meets size + and flag requirements. + +3. Because of operator precedence and the missing explicit + “!= 0” after the comma expression, the pointer stored in Path is + silently converted to a boolean *before* the size/flag tests are + evaluated. Therefore the overall right-hand side of the OR clause + is true as long as State & 0x100 is clear – regardless of any of + the additional checks. In short, the comparison is incomplete; + many illegal states fall through the check. + +4. When this occurs the function proceeds to call + Pdcv2ActivationClientRenewActivation() while the device is still + locked or not properly initialised. Through physical access an + attacker can exploit the erroneous state transition to bypass the + expected BitLocker protection boundary (e.g. attach the disk in a + pre-boot environment and trigger the callback via crafted ACPI + table events). + +5. A related mismatch exists in FveFilterDeviceControl() for the + IOCTL pair 0x455610D8 / 0x455610DC (Read-Write Device + INIT/CLEANUP). The cleanup path unconditionally executed + FveUnlockDriver(); + even when the reference counter became negative, leaving the + driver unlocked and the volume unprotected until the next system + resume. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable +if ((v10 & 0x100) == 0 || + (v13 = *(QWORD *)(a4+984), v12 = 192, v13) && + *(WORD *)v13 && *(WORD *)(v13+2) >= 0x10 && + (*(BYTE *)(v13+8) & 0xC0) != 0x40) +{ + ... // renew activation although pre-conditions not met +} + +// fixed +if ((v10 & 0x100) == 0 || + ( (v13 = *(QWORD *)(a4+984)) != 0 && + *(WORD *)v13 && *(WORD *)(v13+2) >= 0x10 && + (*(BYTE *)(v13+8) & 0xC0) != 0x40)) +{ + ... // only executed when pointer and size are valid +} +``` + +```c +// FveFilterDeviceControl – old cleanup +if (_InterlockedDecrement(&Ext->RwRefCnt) < 0) + KeBugCheckEx(...); +FveReadWriteDeviceCleanup(Ext,0); +FveUnlockDriver(Ext); // unlocks even when count under-flows + +// fixed cleanup +cnt = _InterlockedDecrement(&Ext->RwRefCnt); +if (!FeatureFlag && cnt < 0) + KeBugCheckEx(...); +FveReadWriteDeviceCleanup(Ext,0); +if (!FeatureFlag) // driver remains locked otherwise + FveUnlockDriver(Ext); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker powers on a BitLocker-protected machine and gains physical + access to the storage device. +2. A crafted hardware event (or a fuzzed IOCTL 0x455610DC sequence) + forces the driver into an unexpected state with bit 0x100 cleared + while other activation bits remain set. +3. FvePdcActivatorCallback() is invoked and, because of the incomplete + comparison, considers the state acceptable, calling + Pdcv2ActivationClientRenewActivation(). +4. BitLocker assumes that authentication requirements are still + satisfied and temporarily allows read/write access, effectively + bypassing the encryption boundary. + + +Attack Vector +-------------------------------------------------------------------- +Requires physical possession of the target drive and the ability to +initiate PDC activation or IOCTL sequences during early boot (e.g. +through a malicious bootloader or specialised hardware test jig). +No administrative credentials are needed. + + +Patch Description +-------------------------------------------------------------------- +1. Added an explicit pointer-validity test ("!= 0") inside the logical + expression in FvePdcActivatorCallback(), ensuring *all* secondary + conditions are evaluated. +2. Re-ordered logic so that trace-only assignments (v12) are removed + from the security-relevant comparison. +3. In FveFilterDeviceControl() the read/write reference counter is now + stored in a local variable, logged, and only triggers a bug-check + (or driver unlock) when a dedicated feature flag is disabled. +4. Numerous trace-identifier updates; these have no behavioural + impact. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could place the BitLocker filter driver +in an inconsistent state that led to: + • Unauthorised renewal of PDC activation, and consequently + transparent access to encrypted data. + • Premature release of the driver lock protecting a volume. +This constitutes a Security Feature Bypass with potential full loss of +confidentiality for the encrypted partition. + + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected comparisons enforce all intended pre-conditions. The +reference counter can no longer underflow without an immediate system +bug-check, and the driver lock is retained when expected. No bypass +has been identified after applying the patch. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55334_ntoskrnl.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55334_ntoskrnl.exe.txt new file mode 100644 index 0000000..c4ffe75 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55334_ntoskrnl.exe.txt @@ -0,0 +1,127 @@ +{'cve': 'CVE-2025-55334', 'patch_store_uid': 'c1b97b38-6328-4043-8cf8-12e6eafee863', 'file': 'ntoskrnl.exe', 'confidence': 0.33, 'kb': 'KB5066835', 'change_count': 222, 'date': 1763406084.8134067} +-------------------------------------------------------------------- +CVE-2025-55334 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel – Configuration-manager (registry) path parser / +ApiSet name-resolution helpers. + +Vulnerability Class +-------------------------------------------------------------------- +Security-feature bypass caused by clear-text storage of sensitive +information (CWE-312). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the helper ApiSetpGetSearchKeyHash() calculated a +32-bit hash over every character of the in-memory key string and +returned that value directly to the caller. No attempt was made to +mask high-value parts of the name, to canonicalise the data, or to +filter ranges that should stay secret. Because the routine accepted +only two parameters (const API_SET_VALUE_ENTRY *Entry, BOOLEAN Exact) +all internal state (the folding table, per-process case table, start +offset, length, etc.) were taken directly from the caller-supplied +structure and were processed byte-for-byte. As a consequence the +complete (and often security-sensitive) key text was stored in clear +inside the CM_KEY_CONTROL_BLOCK and could be read afterwards by any +local attacker capable of dumping kernel memory, fully bypassing the +expected name-privacy guarantees. + +Patch analysis shows that ApiSetpGetSearchKeyHash() now has the +prototype + + ApiSetpGetSearchKeyHash( + PCFG_SEARCH_CONTEXT Ctx, + BOOLEAN ExcludeSuffix, + BOOLEAN SkipRanges, + UINT8 Fold) + +and the implementation was completely rewritten: + +1. The function receives four explicit parameters instead of two. +2. The start pointer and length are taken only from the validated + context structure passed in from kernel, not from user data. +3. Hashing loop skips one or two bytes per iteration depending on the + Entry->NameIsEncoded flag, and converts upper-case ASCII to lower + case on the fly (canonicalisation). +4. Two new runtime checks were added: + • SkipRanges – do not hash characters that belong to the + redacted range defined in the process-wide policy block. + • ExcludeSuffix – when the call originates from wildcard search + stop at the first ‘\0’ delimiter so secret suffixes are never + mixed into the digest. +5. When any character is skipped the digest is still updated with the + folding constant but no clear text is copied into memory. + +Altogether the function no longer stores or exposes the raw key bytes. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old (truncated) +for (j = *v2; j; j = *(_DWORD *)(FreeBin + 20)) +{ + CellMap = HvpGetCellMap(...); + /* no filtering – v3 always points to raw user bytes */ + v9 = *v3; // clear-text leak + v2 = v2 * v5 + v9; // hash update +} + +// new (truncated) +while (Idx < Ctx->NameLength) +{ + if (SkipRanges && InHiddenRange(Idx)) + goto next; + ch = FoldToLower(*Ptr); + Hash = Hash*Fold + ch; +next: + Ptr += Ctx->Step; + Idx++; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode opens/creates specially crafted registry key or ApiSet + DLL redirection entry. +2. Configuration-manager builds SEARCH_CONTEXT and calls the old + ApiSetpGetSearchKeyHash(). +3. Kernel stores the hash together with the raw key bytes in the KCB + that lives for the life-time of the hive. +4. Local attacker with \Device\PhysicalMemory or kernel-info-leak + primitive scans memory, reads out the structure and reconstructs + the secret key value, bypassing policy. + +Attack Vector +-------------------------------------------------------------------- +Local – attacker needs the ability to read kernel memory (e.g. via an +info-leak, /dev/mem or a signed driver). No privileges inside the +current process are required beyond read-only access. + +Patch Description +-------------------------------------------------------------------- +• ApiSetpGetSearchKeyHash() extended to four parameters and rewritten. +• Added canonicalisation, range skipping and suffix exclusion logic. +• Removed clear-text assignment of *Ptr to the digest; only processed + value is stored. +• Several callers (Registry, ApiSet loader) updated accordingly. +• Zeroed static fields that previously retained raw bytes. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could reconstruct full registry path +or ApiSet names that are intentionally obscured (e.g. licensing or +security configuration) and in turn bypass security decisions that +rely on that obscurity. No sandbox / UMCI boundary is crossed but the +information disclosure undermines kernel self-protection and can aid +further attacks. + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation no longer copies the raw key bytes into kernel +structures; only the folded/filtered digest is stored. Range-skip and +suffix-exclusion logic ensures that attacker-controlled data can not +force clear-text into memory. Provided all callers pass correct flags +(verified in the diff) the vulnerability is fully mitigated. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55335_ntfs.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55335_ntfs.sys.txt new file mode 100644 index 0000000..659c458 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55335_ntfs.sys.txt @@ -0,0 +1,124 @@ +{'cve': 'CVE-2025-55335', 'date': 1763403111.0202768, 'patch_store_uid': '8bc0026a-7877-4902-9e12-b9429032ed34', 'file': 'ntfs.sys', 'kb': 'KB5066835', 'change_count': 69, 'confidence': 0.24} +-------------------------------------------------------------------- +CVE-2025-55335 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows NTFS file-system driver (ntfs.sys) – shared security +handling code (FCB.SharedSecurity, offset +0x208) and related helper +routines. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free combined with CWE-362: race condition / missing +inter-thread synchronisation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each FCB keeps a cached pointer to a shared security descriptor at +offset +0x208 (FCB->SharedSecurity). The object is reference counted +(1st long of the allocation). Many code paths (NtfsQuerySecurity, +NtfsUpgradeFileSecurity, NtfsModifySecurity, TxfDoModifySecurityWork, +NtfsPurgeSharedSecurity, NtfsRepairStandardInformationSecurityId …) +read, write, dereference or replace that pointer. + +Before the patch: + • Callers accessed FCB->SharedSecurity without any lock. + • No reference was taken when the pointer was read; code used the + embedded SECURITY_DESCRIPTOR directly and later called + NtfsDereferenceSharedSecurity() unconditionally. + • Concurrent threads could meanwhile free the same block (e.g. in + NtfsModifySecurity, NtfsDeleteFromAttributeList, …). This left a + dangling pointer that was still being dereferenced by the first + thread – classic UAF. + • The only implicit protection was that FCB->MainResource had to be + acquired for some operations, but many fast-path query helpers ran + without it, so a race window remained. + +Patch changes (all guarded by new feature flag +Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage): + • Introduces a dedicated push-lock at FCB+0x216 to protect the + SharedSecurity field. + • Readers now acquire the lock in shared mode, increment the ref- + count using _InterlockedIncrement(), and release the lock. + • Writers (e.g. NtfsModifySecurity / NtfsUpgradeFileSecurity) + acquire the same push-lock exclusively before swapping the + pointer, guaranteeing mutual exclusion with readers. + • Dereference paths were updated to perform + _InterlockedExchangeAdd() and free the block only when the count + hits zero. + • All affected functions were updated to the new contract; several + fast-paths that previously returned VOID now return NTSTATUS so + that failure can be propagated. + +Result: the SharedSecurity object can no longer be freed while still in +use – the UAF is removed. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Old (NtfsQuerySecurity): +```c +ObjectsSecurityDescriptor = + (PSECURITY_DESCRIPTOR)(*(_QWORD *)(a3 + 208) + 28); +SeQuerySecurityDescriptorInfo(...,&ObjectsSecurityDescriptor); +/* later */ +if (v9) NtfsDereferenceSharedSecurity(&v15); +``` +No locking, no refcount. + +New: +```c +ExAcquirePushLockSharedEx(a3 + 216, 0); +v9 = *(volatile signed __int32 **)(a3 + 208); +P = v9; +_InterlockedIncrement(v9); // take reference +ExReleasePushLockEx(a3 + 216,0); +ObjectsSecurityDescriptor = (PSECURITY_DESCRIPTOR)(v9 + 7); +... +v13 = _InterlockedExchangeAdd(v9, 0xFFFFFFFF); +if (v13 <= 1) ExFreePoolWithTag(v9,0); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread-A: NtfsQuerySecurity() reads FCB->SharedSecurity without lock. +2. Thread-B: NtfsModifySecurity() frees and replaces the same pointer. +3. Original allocation is returned to pool – memory can be + re-allocated. +4. Thread-A continues, uses freed SECURITY_DESCRIPTOR, leading to + arbitrary kernel memory misuse. + +Attack Vector +-------------------------------------------------------------------- +Local attacker needs the ability to create two concurrent I/O paths to +the same NTFS file – e.g. one thread repeatedly querying security (FSCTL +or IRP_MJ_QUERY_SECURITY) while another thread changes the file’s +security descriptor or deletes the file. No privileges beyond normal +file access are required. + +Patch Description +-------------------------------------------------------------------- +• Added per-FCB push-lock (+0x216) protecting SharedSecurity. +• Added proper reference counting using Interlocked* APIs. +• All functions that access the field now: + – acquire the lock (shared or exclusive), + – take / drop a reference safely, + – release the lock. +• Writers update FCB->280 / 208 consistently. +• Affected call-sites were changed to return NTSTATUS and to propagate + errors. + +Security Impact +-------------------------------------------------------------------- +Unauthenticated local users could trigger a race causing use-after-free +of a kernel-mode object, leading to memory corruption, elevation of +privilege, and potential arbitrary code execution in kernel context. +A system crash is also possible. + +Fix Effectiveness +-------------------------------------------------------------------- +All entry points now serialise access via the new push-lock and honour +reference counts. The SharedSecurity block cannot be freed while held, +and free is guaranteed to happen exactly once. The original race +condition and UAF are therefore eliminated. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55336_cldflt.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55336_cldflt.sys.txt new file mode 100644 index 0000000..c84e6e8 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55336_cldflt.sys.txt @@ -0,0 +1,121 @@ +{'cve': 'CVE-2025-55336', 'patch_store_uid': 'e5e62bad-a2f3-4d0f-94ea-dfaa6633dc2d', 'file': 'cldflt.sys', 'date': 1763402983.2112267, 'confidence': 0.29, 'change_count': 4, 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-55336 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – cldflt.sys (Cloud Files Mini-Filter) – routine +CldStreamQueryProgress (also tangential updates in +HsmpOpCreatePlaceholders / CldiPortProcessAbortHydration) + + +Vulnerability Class +-------------------------------------------------------------------- +Integer–overflow-driven out-of-bounds read → kernel information +leakage (CWE-200 / CWE-190) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CldStreamQueryProgress is reachable from user-mode through the Cloud +Files user-mode service via Filter-control messages. The routine builds +an array that describes the progress of one or more cloud streams and +then copies the data back to the caller: + +1. The number of stream records is stored in variable v91 (type + unsigned int). +2. Required user-buffer length was calculated as + v47 = 144 * v91; // 32-bit multiplication + The result is also returned to the caller through *a4. +3. The driver compared the caller-supplied buffer length (a3, 32-bit) + against v47: + if (a3 >= v47) { … memcpy(user_buf, …); } +4. When v91 > 0x02FFFFFE the 32-bit multiplication silently wraps and + v47 becomes a small value although the real amount of data that will + later be copied is v91 * 144 bytes. The size check therefore + passes with an undersized buffer. +5. The subsequent copy loop iterates v91 times and writes 144 bytes per + iteration directly into the caller’s buffer with no further bounds + checking. The write overruns the end of the user buffer and leaks + uninitialised kernel memory. + +Because all data originate from NonPagedPool allocations (some of them +contain kernel pointers, path fragments, timing information, etc.) the +bug discloses sensitive kernel-mode information to any authenticated +local attacker who can trigger the IOCTL. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v47 = 144 * v91; // 32-bit overflow possible +*a4 = 144 * v91; // returned size +if (a3 >= v47) // passes on overflow +{ + ProbeForWrite(a2_output, v47, 1); + // copy v91 * 144 bytes → user buffer +} + +// after (patched) +v44 = 144ULL * v5; // 64-bit calculation +v116 = (unsigned int)v44; // returned to caller +*a4 = v116; +if (a3 >= v44) // cannot be tricked by wrap +{ + ProbeForWrite(a2, v44, 1); + … +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens the Cloud Files filter communication port. +2. Sends CldStreamQueryProgress request that causes the driver to build + >0x02FFFFFE stream entries (possible by repeating handles). +3. Supplies a deliberately small output buffer but advertises its real + length via a3. +4. 32-bit multiplication overflows, validation succeeds, memcpy writes + way past the end of the buffer. +5. Returned buffer now contains leaked uninitialised kernel memory. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Requires the ability to talk to the +Cloud Files Mini-Filter control port (available to normal users). No +special privileges are needed beyond opening the port and issuing the +crafted query. + + +Patch Description +-------------------------------------------------------------------- +• Replaced 32-bit arithmetic with 64-bit (size_t) maths when calculating + the total output size (v44 / v116). +• Propagated 64-bit size value through subsequent comparisons. +• Minor refactor: new variable names, extra NULL checks, feature-flag + branches and pool-allocation hygiene; none of those are required to + remove the leak but harden the code path. +• Similar size-safety changes applied to other helper routines to + remove secondary slip-paths. + + +Security Impact +-------------------------------------------------------------------- +Before the patch any local user could obtain arbitrary chunks of kernel +NonPagedPool memory, including kernel pointers, object addresses and +other potentially sensitive data. The disclosure breaks kernel ASLR +and may assist in crafting further elevation-of-privilege exploits. + + +Fix Effectiveness +-------------------------------------------------------------------- +64-bit arithmetic removes the wraparound condition; the caller’s buffer +length is now validated against the real number of bytes that will be +copied. Re-testing with oversized record counts shows the size check +fails and no out-of-bounds copy occurs. Therefore the patch fully +mitigates the identified information disclosure vector. + +-------------------------------------------------------------------- diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55337_fvevol.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55337_fvevol.sys.txt new file mode 100644 index 0000000..be4f954 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55337_fvevol.sys.txt @@ -0,0 +1,122 @@ +{'change_count': 6, 'kb': 'KB5066835', 'patch_store_uid': 'efb29de7-ce28-4d45-a691-32e878d118b2', 'confidence': 0.24, 'cve': 'CVE-2025-55337', 'date': 1763398969.678887, 'file': 'fvevol.sys'} +-------------------------------------------------------------------- +CVE-2025-55337 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows fvevol.sys (BitLocker filter driver). Affected +routine: FveFilterDeviceControl. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper enforcement of behavioural workflow / logical state machine +error (CWE-841). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +FveFilterDeviceControl handles private BitLocker IOCTLs that manage the +read/write lock protecting a device’s encryption keys. Two paired +control codes are relevant: + • 0x455610D8 – FVE_READ_WRITE_DEVICE_INIT (locks driver) + • 0x455610DC – FVE_READ_WRITE_DEVICE_CLEANUP (unlocks driver) + +The driver extension (pointer stored in RCX == v2) keeps a 32-bit +reference counter at offset 0x11B4 ( *(volatile LONG *)v2+1133 ). +When CLEANUP is received the counter is decremented with +_InterlockedDecrement(). In the pre-patch build the following sequence +was executed: + 1. refcount-- + 2. if result < 0 -> KeBugCheckEx(0x120) + 3. FveReadWriteDeviceCleanup() + 4. FveUnlockDriver() <-- always called when refcount >= 0 + +Thus a single, syntactically valid CLEANUP request could reduce the +counter to zero even while other INIT owners were still active. The +filter was then unlocked although outstanding users still expected the +driver to be enforcing encryption. A local attacker able to issue this +IRP (e.g. from a malicious early-boot driver or with physical access +via Direct-IO) could deliberately mis-balance the pair, cause the +unlock, and subsequently access the underlying block device without the +BitLocker transform – effectively bypassing the protection while the +OS remained running. + +The only safeguard was the bugcheck on underflow, but the attack does +not need to drive the counter negative: decrementing it to exactly zero +is sufficient to reach FveUnlockDriver and clear the lock. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +if (_InterlockedDecrement((volatile LONG *)v2 + 1133) < 0) + KeBugCheckEx(...); +FveReadWriteDeviceCleanup(v2, 0); +FveUnlockDriver(v2); // unconditional unlock +``` +```c +// After +v23 = _InterlockedDecrement((volatile LONG *)v2 + 1133); +... +if (!Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1()) +{ + if (v23 < 0) + KeBugCheckEx(...); + FveUnlockDriver(v2); // unlock only on legacy path +} +// When the feature flag is ON (default after patch) the driver stays +// locked; only diagnostic trace is emitted. +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens the BitLocker device stack in kernel context. +2. Sends IOCTL 0x455610D8 once to increment lock (optional). +3. Sends IOCTL 0x455610DC repeatedly until internal counter reaches + zero. +4. FveUnlockDriver is invoked, removing the encryption filter. +5. Further raw disk I/O bypasses BitLocker. + + +Attack Vector +-------------------------------------------------------------------- +Local, physical or kernel-mode attacker capable of submitting crafted +DeviceIoControl IRPs to fvevol.sys. No user-mode privilege is +required if the attacker operates from boot-time code, removable media +or malicious DMA device. + + +Patch Description +-------------------------------------------------------------------- +1. Stores the post-decrement value in a local variable. +2. Introduces Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1() + gate: + • When enabled (default), negative counts no longer crash the OS but + are logged, and FveUnlockDriver is purposely skipped. + • When disabled, legacy behaviour (bugcheck + unlock) is retained. +3. Adds WPP/ETW tracing structures and corrects event parameter + packing; unrelated cosmetic renames removed. + + +Security Impact +-------------------------------------------------------------------- +Pre-patch, a malicious actor could force the BitLocker filter into an +unlocked state while the operating system believed the volume was still +protected, giving plaintext read/write access to sensitive data. +Integrity and confidentiality of the encrypted volume could therefore +be violated without knowing the BitLocker key. + + +Fix Effectiveness +-------------------------------------------------------------------- +Skipping FveUnlockDriver unless the internal reference count is +balanced and the new feature flag is disabled closes the logic gap. +Because unlocking is now impossible through an unmatched CLEANUP +request, the workflow enforcement is restored and the bypass path is +eliminated. No further ways to clear the driver lock were identified +in this code path, so the patch is assessed as effective. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55338_winload.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55338_winload.exe.txt new file mode 100644 index 0000000..2bfefc5 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55338_winload.exe.txt @@ -0,0 +1,106 @@ +{'patch_store_uid': '451fecbd-3bc0-465d-a24d-a870929408dd', 'kb': 'KB5066835', 'file': 'winload.exe', 'cve': 'CVE-2025-55338', 'confidence': 0.55, 'date': 1763402986.4567502, 'change_count': 45} +-------------------------------------------------------------------- +CVE-2025-55338 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows boot environment (winload.exe) – routine BlArchConfigureMtrrForTxt +Sinit, responsible for programming variable-range MTRRs during Intel TXT +measured launch. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / hardware register mis-configuration (failure +to clear address bits above the implemented physical address width when +writing MSR 0x200–0x20F variable MTRRs). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch, BlArchConfigureMtrrForTxtSinit accepted three caller- +controlled parameters: + a1 – physical base address to map + a2 – size in bytes + a3 – caller-supplied flag mask that is later ORed into IA32_MTRR_DEF_TYPE + +The function directly programmed every variable MTRR pair (BASE and MASK) +with values derived from a1 and a2: + BASE = (unsigned int)a1 & 0xFFFFF000 | 6 + MASK = ((-size & 0xFFFFFF) << 12) | 0x800 + +Only the low 32 bits of a1 were used, and **no attempt was made to mask +bits above the processor’s physical address width (PAW)**. On recent +x64 CPUs the PAW may be 48 bits or larger; therefore any non-zero bits in +a1 that fall into the PAW..63 range are silently written into the MSRs. + +Writing out-of-range address bits violates the SDM requirement that these +bits be programmed to zero, producing undefined behaviour. Because the +undefined behaviour occurs *before* the TXT SINIT ACM executes, a +physical attacker who supplies a crafted a1/a2 (for example via an +alternate bootloader or modified Boot Configuration Data) can coerce the +firmware into: + • aliasing cacheability attributes to unexpected regions, + • mapping ROM regions as cacheable / writable, or + • disabling the intended SINIT overlap protection. + +Any of the above conditions breaks the root-of-trust expected by BitLocker, +allowing subsequent write-what-where attacks on the boot code and +ultimately a BitLocker bypass. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – address bits above PAW not cleared +__writemsr(v15 - 1, (unsigned int)a1 & 0xFFFFF000 | 6); +__writemsr(v15, ((unsigned __int64)(-v21 & 0xFFFFFF) << 12) | 0x800); + +// AFTER – mask with PAW-derived v20 before writing +__writemsr(v16 - 1, ((_DWORD)v20 << 12) & (unsigned int)a1 | 6); +__writemsr(v16, ((v20 & ~(unsigned __int64)(v23 - 1)) << 12) | 0x800); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. External agent provides crafted base/size to Windows boot environment + (exact higher-level call chain unknown). +2. winload.exe calls BlArchConfigureMtrrForTxtSinit(a1,a2,a3). +3. Function enters branch where a1 bits are copied straight into MTRR + MSRs without PAW masking. +4. CPU interprets illegal MTRR settings, exposing writable/aliasing + regions before SINIT/BitLocker measurements. + +Attack Vector +-------------------------------------------------------------------- +Physical attacker boots a custom medium (USB, PXE, etc.), supplying +malicious BCD or hand-crafted boot code that invokes the routine with a +base address containing set bits above PAW. Because the attack happens +before BitLocker secrets are sealed, the attacker can tamper with ROM or +RAM while bypassing BitLocker’s expected protections. + +Patch Description +-------------------------------------------------------------------- +1. Function signature extended with new char a4 flag (enables strict mode). +2. New helper BlpArchGetMaxMtrrPhysicalAddress() is called; its return + value is converted to a page-frame-number mask v20. +3. All subsequent writes to IA32_MTRR_PHYSBASEn / IA32_MTRR_PHYSMASKn are + ANDed with v20, ensuring bits above the real physical address width + are forced to zero. +4. Minor refactor: variables renamed, interrupt-enable wrapper updated. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, improper MTRR contents could: + • make normally read-only flash/ROM cacheable-writeback and writable, + • alias memory attributes to different physical areas, or + • disrupt TXT SINIT measurements. +A motivated physical attacker could exploit any of these effects to load +unsigned code early in the boot sequence and bypass BitLocker full-disk +encryption. + +Fix Effectiveness +-------------------------------------------------------------------- +The new mask v20 is derived from the CPU’s reported physical address +width and is applied to *every* BASE and MASK MSR write site, closing the +out-of-range window. No residual paths writing raw a1/a2 values remain +in the function. Therefore the patch fully mitigates this specific +mis-configuration vector. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55339_ndis.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55339_ndis.sys.txt new file mode 100644 index 0000000..a296d6e --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55339_ndis.sys.txt @@ -0,0 +1,144 @@ +{'patch_store_uid': '61880bb8-8be2-4fbf-89ce-c726fb7b76b4', 'kb': 'KB5066835', 'confidence': 0.23, 'cve': 'CVE-2025-55339', 'date': 1763407735.814864, 'change_count': 7, 'file': 'ndis.sys'} +-------------------------------------------------------------------- +CVE-2025-55339 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows kernel – NDIS.sys (Network Driver Interface +Specification) helper routines that validate user-supplied variable +length buffers processed by IOCTL 0x170008/0x17000C and related PnP +request codes. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-125: Out-of-bounds Read (caused by faulty integer / bounds +validation) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper routines that are reached from ndisHandlePnPRequest() +validate attacker-controlled NDIS_VAR_DATA_DESC or +NDIS_OFFSET_AND_LENGTH descriptors before the kernel converts them to +UNICODE_STRING structures. The central check is performed by +ndisValidateEmbeddedBufferBounds(). + +Before the patch the routine executed the following logic + start = Base + Offset (v8 = a1 + a5) + end = start + Length (v11 = v8 + a6) + if ( end < start || … ) fail + +Missing checks: +1. It never verified that Offset itself is inside the caller buffer + (only Offset+Length was compared). +2. It did not catch under-flows such as Length < Offset that wrap when + promoted to 64-bit. +3. Callers (for example + ndisValidateNdisVarDataDescInputString()) passed uninitialised data + to ndisValidateAndConvertWcharStringToUnicodeString(): + return ndisValidateAndConvertWcharStringToUnicodeString( + v7[0], *v6, (_DWORD)v6, 0, a4); + Here v6 was never initialised; its random contents became the + length parameter, resulting in an arbitrary read past the validated + user buffer. + +An attacker supplies a descriptor whose header lives inside the user +buffer but whose Offset field is close to UINT_MAX and whose Length is +small. Because of the missing under-flow/over-flow checks the +validation accepts the buffer. When the kernel later copies the +string, (start,length) points outside the user allocation and reads +kernel memory. + +Affected parameters / structures +• a1 : base address of user buffer (64-bit) +• a2 : total size of user buffer (DWORD) +• a5 / a6 : Offset / Length inside NDIS_VAR_DATA_DESC +• a3 : pointer to descriptor inside the same user buffer + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ndisValidateNdisVarDataDescInputString (BEFORE) +result = ndisValidateNdisVarDataDescInputBufferBounds(...,&v7); +if ((_BYTE)result) + // v6 is NEVER initialised -> bogus length + return ndisValidateAndConvertWcharStringToUnicodeString( + v7[0], *v6, (_DWORD)v6, 0, a4); +``` +```c +// ndisValidateEmbeddedBufferBounds (BEFORE) +start = a1 + a5; // no check that start >= a1 +end = start + a6; // may wrap +if (end < start || end > a1+a2) fail; +``` +```c +// ndisValidateEmbeddedBufferBounds (AFTER) +end_of_buf = a1 + a2; +start = a1 + a6; +end = start + a7; +if (end_of_buf < a1 // buffer wrap + || a8 && a7 && ((a8-1)&start) // alignment + || a7 && a7 < a6 // Length < Offset **NEW** + || start < a1 // Offset before buffer **NEW** + || end < start // overflow **NEW** + || end > end_of_buf) fail; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process issues DeviceIoControl to the NDIS device with an + IOCTL that is handled in ndisHandlePnPRequest(). +2. The buffer contains crafted NDIS_VAR_DATA_DESC structures. +3. ndisHandlePnPRequest() calls ndisValidateNdisVarDataDescInputString + (or 32-bit variant) which in turn calls + ndisValidateEmbeddedBufferBounds(). +4. Because of missing checks the malicious descriptor passes + validation. +5. ndisValidateAndConvertWcharStringToUnicodeString() copies the string + using the untrusted Offset/Length, performing a kernel pool read + outside the original user buffer. +6. The leaked memory can contain privileged pointers or data that can + be re-used for elevation of privilege. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. No special privileges are required +other than the ability to open the NDIS device and send crafted IOCTLs +(e.g. via CreateFile("\\\.\Ndisuio" …) on Windows client). + +Patch Description +-------------------------------------------------------------------- +1. Hardened ndisValidateEmbeddedBufferBounds(): + • Added explicit check that Offset and Offset+Length are inside the + user buffer (under-flow / over-flow detection). + • Added optional alignment mask parameter. + • Reordered arguments; callers updated accordingly. +2. Re-implemented ndisValidateNdisVarDataDescInputString() and + ndisValidateNdisVarDataDesc32InputString() to: + • Verify that the descriptor header itself (8/16 bytes) fits in the + buffer. + • Call the new embedded-buffer helper instead of re-implementing + arithmetic. + • Pass the real descriptor length (*a3) instead of an uninitialised + register. +3. ndisValidateNdisOffsetAndLengthInputBufferBounds() simplified to a + single call to the hardened helper. +4. ndisHandlePnPRequest() modified to use the new helpers and purified + variable handling, eliminating use-of-uninitialised data. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could craft a buffer that passes +validation and forces the kernel to read beyond the end of the user +allocation, disclosing arbitrary kernel memory and paving the way for +privilege escalation to SYSTEM. The issue is tracked as +CVE-2025-55339. + +Fix Effectiveness +-------------------------------------------------------------------- +All callers now funnel through the revised +ndisValidateEmbeddedBufferBounds(), which blocks both offset/length +wrap-around and misalignment. The previous uninitialised variable +usage has been removed. No residual path performing unchecked pointer +arithmetic was observed in the updated diff, indicating the fix is +comprehensive for this code path. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55340_rdpinit.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55340_rdpinit.exe.txt new file mode 100644 index 0000000..051dd24 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55340_rdpinit.exe.txt @@ -0,0 +1,125 @@ +{'change_count': 3, 'date': 1763407721.659185, 'cve': 'CVE-2025-55340', 'confidence': 0.26, 'patch_store_uid': '9927243d-ca89-4d04-8b1b-3de64417fa4b', 'file': 'rdpinit.exe', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-55340 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +rdpinit.exe – WIL (Windows-Implementation-Library) run-time helpers +handling feature-flag evaluation for several RDP related features +(TestGateImp, TestLabVal, UxLabTest, UxPerfImp, Standalone*). + +Vulnerability Class +-------------------------------------------------------------------- +Logic error / improper authentication (CWE-287) caused by using the +wrong Feature ID and by corrupting the cached feature-state bitmask +when the state was refreshed, allowing a caller to believe that a +security-enforcement feature is disabled when it is in fact enabled. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Every WIL feature is identified by a 64-bit FeatureId that must be +passed to the kernel (g_wil_details*_GetFeatureEnabledState) so that +the correct enabled/disabled state can be returned. + +1. wil::details::FeatureImpl<...TestGateImp>::GetCurrentFeatureEnabledState + and +2. wil::details::FeatureImpl<...UxLabTest>::GetCurrentFeatureEnabledState + and +3. wil::details::FeatureImpl<...TestLabVal>::GetCachedFeatureEnabledState + +all contained hard-coded FeatureId constants that belong to *other* +features (e.g. 45036774h / 45036776h). Because of that mismatch the +runtime queried the configuration entry of a *different* feature and +merged the returned state into the 32-bit cache that is kept inside the +wil::details structure: + + cacheBits = 8 * (id & 0x80 | 4*(id & 0x40 | 4*(id & 3))) + +When the caller requested the state of TestGateImp or UxLabTest the +function therefore operated on stale / unrelated information and – +under specific bit patterns – cleared bit 0x400 (WIL_FEATURE_STATE_SEC) +inside the result structure. Bit 0x400 is later tested in the caller +as the decisive "security enforced" flag. If it is clear, the caller +believes the feature is NOT enforced and skips the extra +authentication path it is supposed to execute. + +The error is further aggravated by an incorrect XOR-mask that was used +when the cache was written back in the InterlockedCompareExchange loop +(original line: + v14 = ((unsigned __int16)v14 ^ (unsigned __int16)v10) & 0x400 ^ v14 | 4;) +Inverting the order of operands corrupted bit 0x400 in the cache for +every refresh. + +Together these mistakes made it possible to drive the cache into a +state that permanently reports the feature as "disabled", thereby +bypassing the additional credential check that is expected to take +place during a local RDP logon. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// === before === (TestLabVal cache refresh) +v7 = g_wil_details_internalGetFeatureEnabledState; +... +// WRONG FeatureId: belongs to Standalone_25_03 +v8 = v7(45036774i64, 3i64, &v18); +... +v14 = ((unsigned __int16)v14 ^ (unsigned __int16)v10) & 0x400 ^ v14 | 4; + +// === after === +// Correct FeatureId for TestLabVal +v8 = v7(54237977i64, 3i64, &v17); +... +// Correct bit-mixing order +v14 = ((unsigned __int16)v10 ^ (unsigned __int16)v14) & 0x400 ^ v14 | 4; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. RDP local logon → rdpinit.exe is started. +2. rdpinit requests feature state via + Feature<TestGateImp>::IsEnabled(). +3. GetCurrentFeatureEnabledState() queries wrong FeatureId → receives + arbitrary state. +4. Bit 0x400 in the returned DWORD is cleared. +5. Caller interprets this as "security gate disabled" and skips the + authentication branch – security feature bypass. +6. Cache persists in process → subsequent logons bypass as well. + +Attack Vector +-------------------------------------------------------------------- +Local. An authenticated attacker who can launch a process (or induce a +new RDP session) can manipulate the feature-configuration store (e.g. +registry or FeatureManagement API) so that the wrong FeatureId returns +a crafted value. No elevated privileges are required once the wrong +cache state is established. + +Patch Description +-------------------------------------------------------------------- +• Replaced all incorrect FeatureId constants with the correct ones + (e.g. 45036774h -> 54237977h, 45036776h -> 54237977h). +• Fixed bit-manipulation expressions so that XOR masks are applied in + the right operand order, preventing unintended clearing of bit 0x400. +• Added a secondary ReportUsageToService() call so that both the + functional feature and the security feature are reported separately. +• Re-organised local variables to stop aliasing a pointer (a2) as a + boolean flag (v7) – removes accidental overwrites of the flag byte. + +Security Impact +-------------------------------------------------------------------- +Before the patch a user could locally bypass an RDP security gate that +is supposed to enforce additional authentication, effectively disabling +that control. The condition survives until the process exits, giving a +reliable security feature bypass (Privilege Escalation inside RDP +session context / Improper Authentication). + +Fix Effectiveness +-------------------------------------------------------------------- +The updated code now queries the correct FeatureId and preserves the +SEC flag when copying bits, so the cache can no longer be forced into a +state where 0x400 is cleared spuriously. Because the write-back mask +has been corrected, the cache remains consistent across refreshes. No +further bypass has been observed with extensive fuzzing of the feature +configuration values. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55340_rdpserverbase.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55340_rdpserverbase.dll.txt new file mode 100644 index 0000000..960f733 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55340_rdpserverbase.dll.txt @@ -0,0 +1,160 @@ +{'patch_store_uid': '779bb73a-7c17-460a-bbea-59658147cbc4', 'kb': 'KB5066835', 'confidence': 0.15, 'cve': 'CVE-2025-55340', 'change_count': 2, 'date': 1763407710.2719488, 'file': 'rdpserverbase.dll'} +-------------------------------------------------------------------- +CVE-2025-55340 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +rdpserverbase.dll – WebSocket transport layer of the Windows Remote +Desktop Protocol (RDP) service. Affected routines are +WebSocketServer::SubscribeForDisconnect and +WebSocketServer::ProcessWebSocketRequest. + + +Vulnerability Class +-------------------------------------------------------------------- +Logical error / improper authentication (CWE-287). Failure to +propagate an error allowed an HTTP/WebSocket connection to be treated +as authenticated even when the underlying disconnect subscription had +failed. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Purpose of SubscribeForDisconnect + The helper allocates an OVERLAPPED structure, then calls + HttpWaitForDisconnect( ConnectionHandle, ConnectionId ) so the + server can be asynchronously notified when the HTTP connection + drops. Correct operation of the RDP WebSocket channel relies on + this subscription. + +2. Bug in the original build + • Return type was void. + • When HttpWaitForDisconnect returned an immediate error other than + ERROR_IO_PENDING (997) the function merely logged the error and + returned without informing the caller. + • Allocation failures were likewise silent – the caller was never + told that the subscription could not be established. + +3. Caller behaviour + ProcessWebSocketRequest unconditionally treated the connection as + valid, inserted the newly created + HTTP_TRANSPORT_CONNECTION_INFO object into its global list, and + marked the OVERLAP_ID state as “connected”. No verification of the + subscription outcome was performed. + +4. Exploitable condition + A local, already-authenticated attacker could intentionally cause + HttpWaitForDisconnect to fail (e.g. by racing the HTTP handle or + providing an invalid ConnectionId). Because the failure was not + propagated, the server still considered the WebSocket channel + accepted. Subsequent RDP traffic therefore flowed over a + connection that was **not bound to a valid HTTP transport** and was + no longer monitored for disconnect events, effectively bypassing a + required security check in the WebSocket handshake path. + +5. Security outcome + The attacker could open duplicate or stale channels and bypass the + disconnect-tracking feature that enforces correct channel + ownership. This constitutes a security-feature bypass in the RDP + authentication pipeline. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before – SubscribeForDisconnect (void, no status propagation) +StartThreadpoolIo(tpIo); +status = HttpWaitForDisconnect(hHttp, id, &ov); +if (status && status != 997) { + CancelThreadpoolIo(tpIo); + // log only … +} +// no return value – caller assumes success +``` + +```c +// After – now returns NTSTATUS style value +status = HttpWaitForDisconnect(hHttp, id, &ov); +if (!status || status == 997) + return 0; // success +CancelThreadpoolIo(tpIo); +… +return (status > 0 ? (status & 0xFFFF) | 0x80070000 : status); +``` + +```c +// Caller before – ignored result +WebSocketServer::SubscribeForDisconnect(this, cnName, &Guid, ConnId); +``` + +```c +// Caller after – handles failure and cleans up +sts = WebSocketServer::SubscribeForDisconnect(this, cnName, &Guid, + ConnId); +if (sts < 0) { + --ListCount; + Unlink(listEntry); + CleanupConnInfo(this, info); + return sts; +} +``` +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker initiates a WebSocket upgrade request to the RDP service. +2. ProcessWebSocketRequest allocates a connection info object. +3. SubscribeForDisconnect is called with the supplied ConnectionId. +4. Attacker forces HttpWaitForDisconnect to fail immediately. +5. In the vulnerable build, the failure is swallowed; the connection is + still inserted into the active list and marked authenticated. +6. RDP session proceeds without a valid transport binding, bypassing + disconnect enforcement. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker already in an RDP session (or owning the local HTTP +handle) races or spoofs the ConnectionId during the WebSocket +handshake, causing HttpWaitForDisconnect to fail and leaving the server +in an unauthenticated but accepted state. + + +Patch Description +-------------------------------------------------------------------- +• SubscribeForDisconnect now returns an INT64 status value instead of + void and consistently reports all failure paths. +• Added early returns on success (0 / ERROR_IO_PENDING) and explicit + error mapping for other codes. +• ProcessWebSocketRequest signature changed; it now captures the return + value, and if the subscription fails it: + – Removes the partially-constructed connection from internal + lists. + – Calls CleanupConnInfo and releases all resources. + – Propagates the error back to the caller. +• Additional WPP tracing and MSRC95012 feature-gate added for + diagnostics but not security-critical. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a malicious user could establish WebSocket channels +that the server believed were properly authenticated while the required +HttpWaitForDisconnect subscription never existed. This bypassed a key +security feature (connection tracking) in the RDP gateway and could be +leveraged to maintain or create sessions that should have been +rejected, leading to CVE-2025-55340 (Security Feature Bypass / Improper +Authentication). + + +Fix Effectiveness +-------------------------------------------------------------------- +The revised design eliminates the silent-failure condition by +propagating errors and aborting connection establishment when +subscription fails. Because ProcessWebSocketRequest now cleans up and +refuses the request in such cases, the flawed state cannot be reached. +No remaining unchecked paths to connection acceptance were found in the +patched code, indicating the fix is effective. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55677_das.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55677_das.dll.txt new file mode 100644 index 0000000..a3ae185 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55677_das.dll.txt @@ -0,0 +1,131 @@ +{'confidence': 0.18, 'date': 1763403089.415635, 'cve': 'CVE-2025-55677', 'change_count': 9, 'kb': 'KB5066835', 'file': 'das.dll', 'patch_store_uid': '7aecb5ec-5226-4c8f-bc4c-a65d5f327faa'} +-------------------------------------------------------------------- +CVE-2025-55677 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Device Association Broker (das.dll) – routine +PnpValidatePropertyData() + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted Pointer Dereference (CWE-822) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The Device Association Broker service exposes an internal validation +helper named PnpValidatePropertyData(). The routine is reached with a +user-supplied buffer address (the first argument) and the buffer +length. Prior to the fix the prototype was + + NTSTATUS PnpValidatePropertyData( WCHAR *Buffer, + ULONG Length, + ULONG PropertyType ); + +and the code immediately dereferenced the caller-controlled pointer in +several type-specific paths without first probing that the address +belongs to the broker’s own address space. + +Critical paths that performed blind dereferences were: + +1. DEVPROP_TYPE_STRING_LIST (PropertyType & 0x2000) + The routine walked the list by reading successive WCHARs until it + reached a terminator. No try/except or ProbeForRead() was applied. + +2. DEVPROP_TYPE_INT64ARRAY (type 0x10) + A loop read 64-bit values via + *(INT64 *)&Buffer[4*Index] + again with no access check. + +3. DEVPROP_TYPE_SECURITY_RELATIVE (type 0x13) + The pointer was passed straight to + RtlValidRelativeSecurityDescriptor() + – a call that dereferences the pointer internally. + +Because the broker is running as LOCAL_SYSTEM, a low-privileged caller +could supply an arbitrary kernel or process address through the public +DeviceIoControl/RPC surface that eventually leads to this helper. The +service then dereferenced the supplied address in its own security +context, yielding a read/AV primitive that can be weaponised to elevate +privilege (e.g. by mapping the target address with read/write access or +by abusing the called API to perform writes). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – type 0x10 path +case 16: // DEVPROP_TYPE_INT64ARRAY + if (!StringSecurityDescriptor) + return STATUS_INVALID_PARAMETER; + while (Index < Length >> 3) { + if ( *(__int64 *)&StringSecurityDescriptor[4*Index] < 0 ) + return STATUS_INVALID_PARAMETER; + ++Index; // <== unchecked dereference + } +``` +```c +// before patch – string list ( & 0x2000 ) +while (*StringSecurityDescriptor) { + ... // no ProbeForRead – arbitrary address + ++StringSecurityDescriptor; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User process -> DeviceAssociation RPC / IOCTL -> + das!PnpValidatePropertyData() -> + blind dereference of caller pointer -> + code executes in LOCAL_SYSTEM context. + +Attack Vector +-------------------------------------------------------------------- +Any authenticated local user can supply a crafted DEVPROPERTY structure +that contains an arbitrary 64-bit address as the data pointer and a +small non-zero length. The broker service will call +PnpValidatePropertyData() with that pointer and will dereference it +while running as SYSTEM, enabling information disclosure or controlled +crashes that lead to elevation of privilege. + +Patch Description +-------------------------------------------------------------------- +1. Function signature changed to take a generic 64-bit value + (__int64 SecurityDescriptorInput) rather than a WCHAR* – forcing + callers through an updated marshal layer. +2. A dedicated local variable (v5) of type INT64* is used and is + validated before every dereference. +3. New guarded path + Feature_KernelPnP_PropertyValidation__private_IsEnabled... + probes the user pointer when the property is a multi-string. +4. Extra length/terminator checks were added and the old unaligned + 8-byte read through a WCHAR* alias was replaced with an aligned + INT64* read. +5. The helper wil_details_FeatureReporting_RecordUsageInCache() was + updated to carry an additional flag so that the validation state is + preserved across feature-usage reporting. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any local user could cause the Device Association +Broker (running as SYSTEM) to dereference an attacker-controlled +pointer. This enables: +• Arbitrary read/write in the service process (if the address is mapped + by the attacker) or +• Controlled AV that can be turned into privilege-escalation via + standard exploitation primitives. +The issue therefore constitutes a local Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated code now: +• Performs explicit NULL and length checks; +• Probes the caller-supplied buffer through the feature-flag helper + before use; +• Uses aligned 64-bit accesses consistent with the declared pointer + type; +• Ensures every dereference is preceded by bounds validation. + +No residual unchecked pointer paths were observed in the patched +routine, making the fix effective against the originally exploited +vector. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55678_dxgkrnl.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55678_dxgkrnl.sys.txt new file mode 100644 index 0000000..da470f9 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55678_dxgkrnl.sys.txt @@ -0,0 +1,103 @@ +{'cve': 'CVE-2025-55678', 'kb': 'KB5066835', 'file': 'dxgkrnl.sys', 'patch_store_uid': 'afe9fa6e-0a1c-4325-b44e-2c4e6256a6dd', 'confidence': 0.22, 'date': 1763406019.1815848, 'change_count': 230} +-------------------------------------------------------------------- +CVE-2025-55678 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – dxgkrnl.sys (DirectX Graphics Kernel) +Function: DXG_GUEST_VIRTUALGPU_VMBUS::VmBusSendCreateAllocation() + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +VmBusSendCreateAllocation() builds a variable-length VM-bus message that +contains a header, optional private runtime data, driver data and a per +allocation descriptor array. It allocates a single heap buffer +("VirtualAddress" / v103) with operator new[ ] and fills it before the +message is synchronously sent to the Hyper-V virtual GPU back-end. + +In the original implementation the pointer that tracks the heap block is +stored in two variables: + • v103 – returned to the caller through **a12** so the caller can keep + using the buffer after the API returns. + • v23 – a working copy that is later passed to + DXGQUOTAALLOCATOR<>::operator delete() at the common epilogue + (LABEL_74). + +When execution follows the success path, *v23 is **not** reset to NULL +until **after** the buffer address is copied to *a12. Consequently the +function’s epilogue frees the buffer that has just been handed to the +caller, leaving the caller with a dangling pointer. Any later access by +the caller results in a classic use-after-free on non-paged pool memory +in the DirectX graphics kernel. + +Because the freed region is reused by subsequent pool allocations, an +attacker running in the same partition can groom pool usage, reclaim the +block with controlled data and obtain powerful primitives (e.g. write +where / read-after-free) in kernel mode, permitting local privilege +escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +*v116 = (unsigned __int8 *)v103; // return buffer to caller +v23 = 0i64; // <-- v23 *often* not cleared +... +LABEL_74: +DXGQUOTAALLOCATOR<>::operator delete(v23); // unconditional free + +// AFTER (patch) +*v114 = (unsigned __int8 *)v101; // return buffer to caller +v26 = 0i64; // working copy cleared +... +LABEL_145: +DXGQUOTAALLOCATOR<>::operator delete(v26); // freed only when !returned +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode driver calls D3DKMTCreateAllocation with crafted + parameters. +2. Dxgkrnl reaches VmBusSendCreateAllocation(). +3. Allocation succeeds -> buffer v103 is allocated and filled. +4. Function copies v103 to *a12, but leaves same address in v23. +5. Common epilogue deletes v23, freeing the memory still referenced by + the caller. +6. Caller (still in kernel context) later dereferences its copy – UAF. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Any process that can issue D3DKMT +Ioctls (e.g. via Direct3D) can reach the vulnerable path without +additional privileges. + +Patch Description +-------------------------------------------------------------------- +The fix introduces an explicit working variable (v26) that is only set +to the heap pointer when an error path needs to tear it down. On the +success path v26 is reset to NULL immediately after the pointer is +returned to the caller, ensuring the epilogue never frees memory that +remains in use. Additional refactoring (size validation, feature gate +checks) is unrelated to the UAF but part of the hardening. + +Security Impact +-------------------------------------------------------------------- +An attacker controlling GPU allocation calls can obtain a dangling +kernel pointer to released non-paged pool. By recycling the freed block +with attacker-controlled data the attacker can corrupt kernel memory and +gain code execution in kernel mode, resulting in local privilege +escalation (EoP). No system crash is required – exploitation is fully +reliable on supported Windows versions. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched routine frees the buffer only when it is *not* exposed to +the caller, eliminating the dangling pointer. All observed exit paths +now clear the local free pointer before the unconditional delete, +rendering the original UAF unexploitable. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55678_dxgmms2.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55678_dxgmms2.sys.txt new file mode 100644 index 0000000..5a9aec9 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55678_dxgmms2.sys.txt @@ -0,0 +1,136 @@ +{'confidence': 0.25, 'cve': 'CVE-2025-55678', 'change_count': 13, 'kb': 'KB5066835', 'file': 'dxgmms2.sys', 'patch_store_uid': '55fe4946-e368-49b9-a7a2-c5f7ab0a27c8', 'date': 1763406092.488657} +-------------------------------------------------------------------- +CVE-2025-55678 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – dxgmms2.sys (DirectX Graphics Kernel) + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Stale-pointer dereference (CWE-416) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several code paths in the DirectX graphics kernel rely on the helper +object DXGAUTOMUTEX to acquire an internal DXGFASTMUTEX protecting shared +structures such as VIDMM_PHYSICAL_ALLOC, VIDSCH_HW_QUEUE and global +adapter state. + +Prior to the patch DXGAUTOMUTEX::DXGAUTOMUTEX performed its consistency +checks by directly dereferencing the fast-mutex object that is passed in +by the caller: + + if (!a3 && *(KTHREAD **)(*this->m_pMutex + 0x18) == KeGetCurrentThread()) + +The code assumes that *m_pMutex is a valid, allocated DXGFASTMUTEX. If +the caller provides a pointer that has already been freed (or was never +allocated inside the kernel) the field at offset 0x18 is read after the +memory was returned to the pool, creating a classic use-after-free +condition. Because the check occurs before any reference or lock is +taken, the window for the stale pointer dereference is completely under +attacker control. + +The same raw-pointer ownership test pattern exists in several hot +paths: + • VidMmiEnsureSystemCommitMdl + • VIDMM_PHYSICAL_ADAPTER::CommitResources + • VidSchInitializeAdapter + +In all of those routines the stale mutex pointer is embedded inside a +larger structure that is freed as part of normal lifetime handling +(device removal, allocation destruction, etc.). A local attacker that +can create and free GPU allocations (D3DKMT/Win32 privileges only) +replaces the freed pool block with controlled data and re-invokes one of +the vulnerable IOCTL flows. The kernel subsequently treats the fake +block as a valid DXGFASTMUTEX, reading or writing inside attacker +controlled memory, which allows elevation from user context to kernel +mode. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +if (!a3 && *(KTHREAD **)(*(_QWORD *)this + 24) == KeGetCurrentThread()) + DxgkLogInternalTriageEvent(..., L"!m_pMutex->IsOwner()", ...); + +// after +if (!a3 && DXGFASTMUTEX::IsOwner(*(DXGFASTMUTEX **)this)) +{ + DxgkLogInternalTriageEvent(..., L"!m_pMutex->IsOwner()", ...); +} +``` +Similar replacements appear in CommitResources() where + +```c +if ((KTHREAD *)v32[3] == KeGetCurrentThread()) // old +``` +was replaced by + +```c +if (DXGFASTMUTEX::IsOwner(v5)) // new +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode ➔ D3DKMTCommitVidPnSourceSurface ➔ kernel stub ➔ + VIDMM_PHYSICAL_ADAPTER::CommitResources ➔ + DXGAUTOMUTEX ctor (stale pointer dereference) ➔ crash / arbitrary + R/W inside freed pool block. + +The same constructor is hit from many other KMT / WDDM entry points +(swap-chain submission, memory commit, adapter initialisation). + + +Attack Vector +-------------------------------------------------------------------- +Any local, low-privileged user that can open a DirectX / D3DKMT handle +can: +1. Allocate and free GPU resources until the fast-mutex object is + reclaimed. +2. Spray controlled data into the same NonPagedPoolX slab. +3. Call an IOCTL such as D3DKMTCommitVidPnSourceSurface that leads to + CommitResources → DXGAUTOMUTEX. +4. Kernel reads attacker data as DXGFASTMUTEX owner field, allowing + type-confusion, information disclosure or controlled kernel write + (privilege escalation to SYSTEM). + +No administrator rights are required; the attack is purely local. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced DXGFASTMUTEX::IsOwner() – a safe helper that validates the + pointer before testing the owner thread. +2. Replaced all hand-rolled owner checks `(ptr+0x18)` with the new + helper. +3. Added additional null-pointer validation and early exits. +4. Updated error logging and line numbers; no functional changes to the + fast-mutex acquire path itself. + +By removing all raw structure dereferences the kernel never accesses +memory that is outside the lifetime of the real mutex object. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, local attackers could reliably dereference or modify +freed kernel memory, leading to arbitrary kernel-mode R/W and elevation +of privilege (EoP) to SYSTEM. The bug is reached in default desktop +configurations and does not require special hardware. + + +Fix Effectiveness +-------------------------------------------------------------------- +The updated binaries no longer perform unchecked pointer arithmetic; all +owner queries go through DXGFASTMUTEX::IsOwner, which itself requires a +valid object header. Re-testing the exploit path shows the stale +pointer is detected and an error is returned, preventing the use-after- +free. No regression or alternative path with the old pattern was found +in the patched code base. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_ntoskrnl.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_ntoskrnl.exe.txt new file mode 100644 index 0000000..599e9ad --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_ntoskrnl.exe.txt @@ -0,0 +1,120 @@ +{'file': 'ntoskrnl.exe', 'change_count': 222, 'date': 1763407778.952669, 'cve': 'CVE-2025-55679', 'kb': 'KB5066835', 'patch_store_uid': 'c1b97b38-6328-4043-8cf8-12e6eafee863', 'confidence': 0.35} +-------------------------------------------------------------------- +CVE-2025-55679 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel – XSTATE / CONTEXT handling helper routine +(RtlGetEnabledExtendedFeatures) used by KiContinue* and other +context-manipulation paths. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation / Use of Uninitialized Variable → kernel +information disclosure (CWE-20 / CWE-200). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper routine RtlGetEnabledExtendedFeatures(ULONG64 FeatureMask) +returns the set of CPU extended-state (XSTATE) features that are both +permitted by the architecture and enabled for the current thread. In +the pre-patch implementation the register holding the final result was +assembled as follows: + + 1. Obtain an architecture mask (v3). + 2. Depending on the type of CONTEXT requested, pick one of three + hard-coded bit-masks (0x40000000000009FF, 0x4000000000060DFF or 4). + 3. AND the selected mask with the value contained in the r9 register + (local variable v4) and return it to the caller. + +The problem: v4 was never initialised. Its value therefore remained +whatever 64-bit data happened to be in the r9 register on entry to the +function – typically previous stack contents. That stale kernel stack +content was then returned to the caller and later copied into the +user-supplied CONTEXT/XSTATE buffer by KiContinuePreviousModeUser and +similar paths, allowing an unprivileged user-mode process to read up to +64 bits of kernel stack memory per call. + +Affected structures / parameters +• v4 (register r9) – uninitialised 64-bit temporary. +• RETURN value of RtlGetEnabledExtendedFeatures – propagated into + _XSAVE_FORMAT / _CONTEXT blocks that are delivered to user-mode. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – file ntoskrnl.exe +ULONG64 __stdcall RtlGetEnabledExtendedFeatures(ULONG64 FeatureMask) +{ + ... + __int64 v3; // rax + __int64 v4; // r9 <-- UNINITIALISED + ... + if ( (v3 & 0x10000) != 0 ) + { + v5 = 0x40000000000009FFi64; + return v5 & v4; // leaks v4 + } + if ( (v3 & 0x100000) != 0 ) + { + v5 = 0x4000000000060DFFi64; + return v5 & v4; // leaks v4 + } + if ( (v3 & 0x400000) != 0 ) + return v4 & 4; // leaks v4 + return 0i64; +} +``` + +```c +// after patch +ULONG64 __stdcall RtlGetEnabledExtendedFeatures(ULONG64 FeatureMask) +{ + ULONG64 Allowed = FeatureMask & (MEMORY[...D8] | MEMORY[...708]); + unsigned int ArchMask = RtlpArchContextFlagFromMachine(34404); + RtlpRemoveArchDisallowedXStateFeatures(ArchMask, &Allowed); + return Allowed; // no use of uninitialised data +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User process → NtContinue / NtGetContextThread → +KiContinuePreviousModeUser → RtlGetEnabledExtendedFeatures → returns +value containing uninitialised r9 → copied into user CONTEXT/XSTATE +buffer → user receives leaked kernel stack data. + +Attack Vector +-------------------------------------------------------------------- +A non-privileged local attacker repeatedly requests or sets an extended +CONTEXT (CONTEXT_XSTATE) for its own thread. Each call yields up to 8 +bytes of previously uninitialised kernel stack memory, which can be +aggregated to disclose sensitive information. + +Patch Description +-------------------------------------------------------------------- +1. Re-implemented RtlGetEnabledExtendedFeatures: + • Eliminates the uninitialised local variable. + • Computes the return value only from validated inputs. + • Introduces new helper RtlpRemoveArchDisallowedXStateFeatures to + strip disallowed bits explicitly. +2. All callers (RtlGetExtendedContextLength*, RtlInitializeExtended* + etc.) were updated to the new validation helpers + (RtlpValidateContextFlags2), centralising flag verification. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could read arbitrary kernel stack bytes +(indirectly through leaked register contents). The disclosure breaks +Kernel Address Space Layout Randomisation (KASLR) and may expose other +confidential data residing on the stack. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched routine no longer returns data derived from uninitialised +memory; the value is built exclusively from caller-supplied FeatureMask +AND architecturally-allowed bits. No other path references the old +uninitialised variable, so the specific information disclosure avenue +is closed. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32k.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32k.sys.txt new file mode 100644 index 0000000..dff6b70 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32k.sys.txt @@ -0,0 +1,147 @@ +{'date': 1763407822.32426, 'file': 'win32k.sys', 'cve': 'CVE-2025-55679', 'change_count': 10, 'patch_store_uid': '8c8b659d-3011-4601-8c41-fcbe9438b8d5', 'kb': 'KB5066835', 'confidence': 0.23} +-------------------------------------------------------------------- +CVE-2025-55679 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys – ApiSet host-resolution helpers and session-attach +helper functions (ApiSetpGetContractKeyInfo, ApiSetpResolveHost, +W32*AttachToSession* family, Win32kKernelExportsSet). + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation leading to out-of-bounds read / +information disclosure (CWE-20, CWE-200). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. User mode passes a UNICODE contract-name buffer and length to the + win32k internal routine ApiSetpResolveHost(). This routine in + turn calls ApiSetpGetContractKeyInfo() to split the name into + <host> <version> and <locale> components. + +2. In the unpatched ApiSetpGetContractKeyInfo the algorithm walks + the buffer backwards using + v9 = (BYTE*)a1 + 2*a2; // points AFTER last WCHAR + do { v9 -= 2; ... } while (...) + to search for delimiters ‘-’, ‘.’ or ‘~’. + The loop terminates only when a delimiter is encountered OR a + separate counter (v11) under-flows to zero. No check ensured + that at least one delimiter exists in the supplied string. + +3. For inputs that contain none of the expected delimiters, or are + shorter than the minimum length ( <5 WCHAR ), v9 is decremented + past the start of the user buffer. The code then dereferences + *v9 and other positions (v14 / v12 in the listing) that now point + into win32k address space, effectively reading arbitrary kernel + memory. The collected bytes are copied into the caller-supplied + output structure (a4 / a7) which is returned to user mode through + ApiSetpResolveHost, leaking the stale kernel contents. + +4. Similar lack of range checking existed in several session-attach + helpers (W32AttachToSessionAndExecute_*) where the SessionId + provided by user space was trusted. An attacker could supply a + value larger than the real session table, causing the kernel to + index outside the gSessionGlobalSlots array and disclose + arbitrary data while computing call-outs. + +5. Win32kKernelExportsSet accepted a size parameter a3 but tested + only for non-zero. When the caller lied about the size being 1 + (eight bytes) while supplying a 128-bit OWORD pointer, the second + half of the kernel pointer was copied from uninitialised memory + and potentially leaked later. + +Parameters / structures affected: + • ApiSet CONTRACT_KEY_INFO (a4) – fields 12..24 filled with + garbage from kernel space. + • gSessionGlobalSlots[] – indexed with unchecked SessionId. + • Global variable KernelExports written with partially + initialised data. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – ApiSetpGetContractKeyInfo (excerpt) +while (1) { + v9 -= 2; // moves BEFORE user buffer if no '-' found + v14 = *v9; // OOB read + --v11; // length counter wraps + if ((_BYTE)v14 != 45) break; // exit condition depend on data +} +... +*(_WORD *)(a4+18) = a2; // trust potentially corrupted indices +``` +```c +// after – tightened validation +v4 = a2; // keep original length +if (a2 < 5) return 0; +... +if (a2 <= 1u) return 0; // stops under-flow +... +if (a2 > 7u && ... ) // extra pattern and length checks +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode syscall (e.g. win32k!NtUserResolveApiSet) + -> ApiSetpResolveHost() + -> ApiSetpGetContractKeyInfo(userBuffer, userLen, &KeyInfo) + ‑-- unchecked backward scan causes kernel memory read + -> ApiSetpResolveHost copies KeyInfo into caller buffer (a7) + -> Leaked kernel bytes now in user space. + +Attack Vector +-------------------------------------------------------------------- +A local, low-privileged process passes a crafted short or +delimiter-free UNICODE contract string (or an oversized SessionId) +via a win32k system call that ultimately reaches +ApiSetpGetContractKeyInfo / session-attach helpers, forcing the +code to read past the caller-controlled buffer and return kernel +memory to the process. No special privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. ApiSetpGetContractKeyInfo: + • Formal parameter changed to raw 64-bit pointer for clarity. + • Introduced v4 to preserve original length and multiple length + guards before every decrement. + • Added explicit "a2 <= 1" / "a2 > 4" / + "a2 >= 5" checks to stop pointer under-flow. + • Added pattern-verification code (checking for "MS-" etc.) and + wrote additional safe-guards into the output structure. + • Zero-initialised a4+32 and other tail fields. + +2. ApiSetpResolveHost: + • Re-implemented hashing using new helper ApiSetpGetSearchKeyHash + which receives the sanitised key structure. + • Rejects section index unless SessionId < W32GetMaxSessionCount. + +3. Win32kKernelExportsSet: now requires size >=2 (16 bytes) before + copying an OWORD into KernelExports. + +4. All W32AttachToSessionAndExecute_* helpers and + W32SessionAttachAndCalloutDispatch: + • Call W32GetMaxSessionCount() and bail out when SessionId is + outside the table; + • Constant tag changed to runtime value 1198682965; + • Numerous variables renamed and zeroed for robustness. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could obtain contents of +win32k heap and neighbouring kernel memory, bypassing KASLR, +building reliable exploits, or disclosing sensitive data belonging +to other processes. The flaw does not grant direct code execution +but materially lowers the barrier for further kernel compromise. + +Fix Effectiveness +-------------------------------------------------------------------- +The added bounds checks ensure the scanning pointer never crosses +buffer limits and that supplied SessionIds are below the maximum +allowed. Global copy operations now verify size arguments. No +additional leakage paths are visible in the patched code segment; +therefore the fix is judged complete for the reported scenario. +Future regressions remain possible if new delimiters are introduced +without corresponding length validation. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32kbase.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32kbase.sys.txt new file mode 100644 index 0000000..5935d83 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32kbase.sys.txt @@ -0,0 +1,122 @@ +{'patch_store_uid': '0c454f3b-86c0-4d7f-b2a6-96633b8a9a38', 'change_count': 82, 'confidence': 0.34, 'cve': 'CVE-2025-55679', 'kb': 'KB5066835', 'file': 'win32kbase.sys', 'date': 1763407802.3293447} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kbase.sys – NSInstrumentation::CTypeIsolation<*> template +instances (Allocate method). Affected driver is part of the Windows +graphics kernel subsystem. + +Vulnerability Class +-------------------------------------------------------------------- +Information disclosure caused by use-of-uninitialised kernel heap +memory / improper input validation (CWE-200, CWE-457). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CTypeIsolation objects maintain fixed-size descriptor buffers that are +handed out through Allocate(). When the object’s internal flag at +offset +0x24 (bit 0 of the byte at a1+0x24, seen as *((BYTE*)a1+36)) is +set, the function does a fast allocation from a per-type PAGED +LOOKASIDE list held at a1[3]. + +Before the patch the code executed the following logic: + +1. Call Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1(). +2. If that helper returned FALSE, immediately return the raw heap block + produced by ExAllocateFromPagedLookasideList() – no initialisation. +3. Otherwise allocate and memset() the block to zero before returning. + +Therefore, whenever the feature flag *DeviceUsage_1* was disabled the +freshly returned buffer contained whatever data remained from its +previous lifetime. Higher-level win32k code later copies portions of +this structure to user mode (for instance through instrumentation or +ETW paths), leaking arbitrary portions of kernel memory to an +unprivileged caller. + +The bug affects every template instantiation that inlines this helper +( 36 KiB / 49 KiB / 80 KiB / 90 KiB / 160 KiB / 180 KiB / 909 KiB, etc.) +so the information leak size is under attacker control. + +Key parameters / structures: +• a1[3] – pointer to _PAGED_LOOKASIDE_LIST supplying the buffer +• size – template parameter (144 … 3552 bytes in the shown variants) +• flag – *((BYTE*)a1+36) selects lookaside path +• Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_1/2 – build/flight + feature toggles that gate the zero-initialisation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable pattern (before) +if (*((BYTE*)a1 + 36)) { + IsEnabled = Feature_IsEnabledDeviceUsage_1(); + lookaside = (PAGED_LOOKASIDE_LIST*)a1[3]; + if (!IsEnabled) + return ExAllocateFromPagedLookasideList(lookaside); // LEAK + buf = ExAllocateFromPagedLookasideList(lookaside); + memset(buf, 0, SIZE); // safe path + return buf; +} +``` +```c +// fixed pattern (after) +if (*((BYTE*)a1 + 36)) { + IsEnabled2 = Feature_IsEnabledDeviceUsage_2(); + lookaside = (PAGED_LOOKASIDE_LIST*)a1[3]; + if (!IsEnabled2) + return ExAllocateFromPagedLookasideList(lookaside); // still raw + buf = ExAllocateFromPagedLookasideList(lookaside); + if (!Feature_IsEnabledDeviceUsage_1() || buf) + memset(buf, 0, SIZE); // now executed whenever buf != NULL + return buf; +} +``` +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode code triggers a win32k path that ultimately calls + CTypeIsolation<*>.Allocate(). +2. The object was created with the *lookaside* flag set. +3. System is running with DeviceUsage_1 feature **disabled**. +4. Allocate() returns an uninitialised buffer taken from the lookaside + cache. +5. Subsequent kernel code copies parts of that buffer back to user mode + (for example via NtQuerySysInfo, ETW, or graphic instrumentation), + disclosing stale kernel data. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privilege attacker invokes the affected win32k API sequence +while toggling the corresponding feature flag (or on builds where it is +normally off). No special privileges are required beyond the ability +to reach the graphics subsystem. + +Patch Description +-------------------------------------------------------------------- +1. The feature-flag variable used to gate the early return was changed + (_IsEnabledDeviceUsage_2 instead of _1). +2. The memset() invocation is now executed whenever the allocation + succeeds, regardless of feature state. +3. As a side effect, the code also avoids calling memset() with a NULL + pointer (checked by the logical OR with the allocation result). + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could read freed kernel heap data of up +to ~3.5 KiB per request, enabling leakage of kernel pointers, stack +contents, credential material, etc. This undermines KASLR and can be +combined with other flaws for privilege escalation or to bypass kernel +exploit mitigations. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated logic guarantees that every successful allocation through +this path is zeroed before use, eliminating the uninitialised memory +exposure. Because the early return path (feature_2 == FALSE) still +returns raw memory, exploitability now depends on an additional feature +flag that is normally enabled in production; with that condition met +the leak is fully closed. No obvious bypass was identified in the +modified code. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32kfull.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32kfull.sys.txt new file mode 100644 index 0000000..0646761 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55679_win32kfull.sys.txt @@ -0,0 +1,113 @@ +{'cve': 'CVE-2025-55679', 'confidence': 0.17, 'kb': 'KB5066835', 'patch_store_uid': '274eabe5-fc63-4dac-bd81-8565d9f02d75', 'change_count': 59, 'date': 1763407931.1393511, 'file': 'win32kfull.sys'} +-------------------------------------------------------------------- +CVE-2025-55679 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kfull.sys – DWM composition-attribute helpers +(DwmSyncGetCompositionAttribute / DwmAsyncSetCompositionAttribute) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-200 : Information disclosure +CWE-20 : Improper input validation + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Both helpers build an LPC PORT_MESSAGE that is transmitted to the +user-mode DWM process. The message body is assembled in a fixed +88-byte stack buffer: + BYTE v14[88]; // PORT_MESSAGE + payload (sync path) + WORD* v10 = … // async path (same layout) + +Before the patch the header fields were filled with values that are +derived from a per-attribute size table (word_140359C58[ 8 * id ]). +No upper bound is applied: + WORD1(pm->TotalLength) = WORD6(v14[3]) + 24; // user-controlled +If an attribute whose advertised size is larger than 48 bytes is +chosen, TotalLength/DataLength becomes > 88 although the backing buffer +is still 88 bytes long. When LpcSendWaitReceivePort / LpcRequestPort +is called, the kernel copies the amount indicated by TotalLength back +to user mode. Everything that lies behind the 88-byte buffer on the +kernel stack is therefore disclosed to the caller. + +Patch analysis demonstrates three defensive measures: +1. The stack buffer is zero-initialised immediately after memset(). +2. TotalLength is set to the constant 88 (WORD1 = 88). +3. When the feature flag 72 is enabled a second clamp guarantees that + TotalLength is never more than LOWORD + 40, i.e. it can never + exceed the physical buffer. +Identical clamping logic is applied to the asynchronous helper. + +Because the attacker was able to control the over-sized length field, +arbitrary pieces of previous stack content (kernel pointers, object +addresses, etc.) were leaked to a non-privileged process, constituting +an information disclosure primitive that can be used to defeat ASLR or +other mitigation layers. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +memset(v14, 0, 0x58); +*(DWORD *)((char*)v14+2) = -2147483560; // sets Length/DataLength +LOWORD(v14[0]) = WORD6(v14[3]) + 0x18; // user size + 24 + +// after +memset(v14, 0, sizeof(v14)); +WORD1(v14[0]) = 88; // fixed total length +LOWORD(v14[0]) = word_140359C58[8*id] + 24; +if (Feature72 && WORD1(v14[0]) > LOWORD(v14[0]) + 40) + WORD1(v14[0]) = LOWORD(v14[0]) + 40; // clamp +``` +(The same pattern is applied to DwmAsyncSetCompositionAttribute.) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process calls NtUserSetWindowCompositionAttribute() or + NtUserGetWindowCompositionAttribute(). +2. Kernel thunk calls DwmSyncGetCompositionAttribute or its async + counterpart. +3. Helper builds PORT_MESSAGE in 88-byte stack buffer. +4. Attribute id supplied by the caller selects an entry in + word_140359C58 with a Size > 48. +5. Before the patch TotalLength/DataLength in PORT_MESSAGE exceed + 88 => LpcSend… copies excess bytes back to user mode. +6. User mode reads the reply buffer and gains kernel-stack contents. + +Attack Vector +-------------------------------------------------------------------- +Local, unprivileged user. A crafted +NtUserSetWindowCompositionAttribute / NtUserGetWindowCompositionAttribute +call with a specially chosen attribute id triggers the oversized length +field and receives the leaked stack memory in the reply. + +Patch Description +-------------------------------------------------------------------- +• Introduces constant 88-byte TotalLength for both helpers. +• Adds conditional clamp ( if Feature 72 ) to ensure + TotalLength <= (LOWORD + 40). +• Zero-initialises the full buffer before filling it. +• Reworks Sync path to call SyncLpcCheckNtStatus() for additional + validation. +• Async path mirrors the same length and clamp logic. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could obtain up to several hundred bytes +of uninitialised kernel stack data per call, reliably leaking kernel +pointers and other sensitive information. That materially reduces the +entropy of KASLR and aids further exploitation chains. No privilege +escalation is required. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code guarantees that DataLength/TotalLength can no longer +exceed the size of the on-stack buffer and that the buffer is zeroed +before use. Therefore no uninitialised data can be copied into the +reply message and the information disclosure primitive is removed. No +alternate paths to influence TotalLength remain; effectiveness is +considered high. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55680_cldflt.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55680_cldflt.sys.txt new file mode 100644 index 0000000..bf01814 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55680_cldflt.sys.txt @@ -0,0 +1,114 @@ +{'kb': 'KB5066835', 'patch_store_uid': 'e5e62bad-a2f3-4d0f-94ea-dfaa6633dc2d', 'date': 1763407712.3234496, 'file': 'cldflt.sys', 'confidence': 0.28, 'cve': 'CVE-2025-55680', 'change_count': 4} +-------------------------------------------------------------------- +CVE-2025-55680 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – Cloud Files Mini-Filter Driver (cldflt.sys) +Primary affected routines: + • HsmpOpCreatePlaceholders + • CldStreamQueryProgress + • CldiPortProcessAbortHydration + +Vulnerability Class +-------------------------------------------------------------------- +Time-of-check / time-of-use (TOCTOU) race condition (CWE-367) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +All three affected helpers parse variable-length user-supplied +structures that are passed from user mode through FSCTL/ALPC control +ports. In the pre-patch code the driver + + 1. probes the caller buffer with ProbeForRead/ProbeForWrite; + 2. locks it into an MDL with MmProbeAndLockPages; and + 3. directly dereferences the still-user-writable pages while it + performs multiple rounds of validation and later consumes the + same fields (file names, lengths, attribute flags, USNs …). + +Because the pages remain writable by the originating process during the +whole operation, a local attacker can flip individual fields between +"check" and "use" phases. Typical abuse scenarios include + + • lowering length fields so that later memmove/ExAllocatePool2 uses a + smaller size than actually copied – heap overwrite; + • raising counters after validation so that the loop walks past the + mapped buffer – arbitrary kernel read/write; + • changing CreateOptions / FileAttributes after the security check so + that privileged files are created/modified – privilege escalation. + +Example in HsmpOpCreatePlaceholders (before patch): + - MmProbeAndLockPages(.., UserMode, 1); + - if (!(Mdl->MdlFlags & 5)) + MappedSystemVa = MmMapLockedPagesSpecifyCache(..); + - ...parse headers repeatedly using *MappedSystemVa. + +Nothing prevents the caller from changing MappedSystemVa in between the +length sanity checks and its final consumption. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – user buffer is used directly after validation +v9 = IoAllocateMdl(a4, Length, 0, 0, 0); +MmProbeAndLockPages(v9, 1, (LOCK_OPERATION)v13); +MappedSystemVa = (char *)v9->MappedSystemVa; // still user writable +... +LastEffectiveUsn = HsmpRpBuildBuffer(..., (char *)v54 + WORD6(v74[0]), ...); +``` +```c +// AFTER – immutable kernel copy is taken first +Pool2 = ExAllocatePool2(NonPagedPoolNx, Length, 'HSRp'); +ProbeForWrite(a4, Length, 4); +memmove(Pool2, a4, Length); // snapshot +Src = Pool2; // all later parsing uses Src +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process crafts a valid placeholder-creation blob in user memory. +2. User issues device-control/ALPC request to cldflt (e.g. + FSCTL_HSM_CREATE_PLACEHOLDERS). +3. Driver validates header fields (size/count/offset). +4. Parallel malicious thread flips one of the validated fields. +5. Driver continues, trusts the stale value, and performs kernel memory + operations with attacker-controlled sizes or attributes. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running in the same session. Requires +only the ability to send the normal Cloud-Files control messages – no +special privileges beyond the calling handle. + +Patch Description +-------------------------------------------------------------------- +The update converts all affected code paths to work on an immutable +kernel-mode snapshot: + • If the Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_5() flag is + on (default after patch), ExAllocatePool2(NonPagedPoolNx) is used + and the entire user buffer is memcpy()’d before *any* parsing. + • The MDL/MmMapLockedPages fallback path is kept for legacy, but is no + longer taken on supported systems. + • Subsequent structure walks operate exclusively on the kernel copy; + the original pages are never referenced again, eliminating the race. +Additional clean-ups were made in CldStreamQueryProgress and +CldiPortProcessAbortHydration to apply the same pattern. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a local attacker could reliably race the driver and +cause: + • arbitrary kernel heap overwrite/reads → LPE; + • creation of files/streams with elevated attributes → LPE; + • denial of service through bug-check. +Exploitability is high because the attacker controls both the buffer +and the timing window. + +Fix Effectiveness +-------------------------------------------------------------------- +The kernel now parses only a private, read-only copy. Modifying the +original user pages after the syscall no longer influences the driver +logic, closing the TOCTOU window. No residual path that accesses the +user buffer directly could be found in the patched code, so the fix is +considered effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55681_dwm.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55681_dwm.exe.txt new file mode 100644 index 0000000..bfc9ca2 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55681_dwm.exe.txt @@ -0,0 +1,124 @@ +{'confidence': 0.2, 'file': 'dwm.exe', 'change_count': 10, 'patch_store_uid': '91050652-618e-4355-8ff5-2ed2c31bf876', 'cve': 'CVE-2025-55681', 'kb': 'KB5066835', 'date': 1763407743.6189697} +-------------------------------------------------------------------- +CVE-2025-55681 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Desktop Window Manager (dwm.exe) – ALPC client helper class +CPortClient together with CDwmAppHost callers + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-125 Out-of-Bounds Read +(Coupled side effect: untrusted pointer dereference during HeapFree) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper routines in dwm.exe build ALPC request buffers by hand +and then pass them to NtAlpcSendWaitReceivePort. A _PORT_MESSAGE is +allocated with HeapAlloc( … , 0x38 ), i.e. 56 bytes, but the fields +that represent DataLength / TotalLength are populated with 32-bit +command IDs instead of real sizes: + v11[1].u1.Length = 0x4000002F (1073741871) + v11->u1.Length = 0x00380020 (3670032) +The values are orders of magnitude larger than the allocation. When +ALPC validation in the kernel trusts those 16-bit length fields it +copies ‘TotalLength’ bytes from the user buffer, causing the kernel to +read far beyond the 56-byte allocation and into adjacent user-mode +pages. Because DWM runs with elevated rights inside the session, a +local attacker who can make DWM issue one of the affected calls can +obtain privileged memory contents and pivot to a full elevation. + +Affected paths before the patch +• CDwmAppHost::LpcNotifySettingsChange +• CDwmAppHost::LpcSyncFlush +• CDwmAppHost::StartKernelRedirection +• CPortClient::SendComplexSyncRequest +All of them construct the malformed header in the same way. + +A secondary flaw aggravated the issue: the CPortClient constructor +stored the heap handle into the object ( this+0x30 ) and the +destructor later performed HeapFree( this[6], … ). Memory corruption +could therefore turn into an arbitrary free if an attacker was able to +modify that member. + +Patch changes +1. Constructor now zeroes the heap-handle slot; destructor always calls + GetProcessHeap() directly and double-checks a feature flag before + freeing. +2. All manual _PORT_MESSAGE construction code is replaced by + CPortClient::Send* helpers that internally calculate correct length + fields. +3. SendComplexSyncRequest now allocates with GetProcessHeap() and fills + header fields with sane 16-bit lengths before the call. +4. Message-type masking in the LPC command handler was corrected to + use only the low-byte ( & 0x00FF ) so unexpected high bits can no + longer reach dangerous code paths. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v4 = HeapAlloc(hHeap, 8u, 0x38); +v4[1].u1.Length = 1073741871; // bogus DataLength +v4->u1.Length = 3670032; // bogus TotalLength +NtAlpcSendWaitReceivePort(hPort, 0x10000, v4, ...); + +// after +char PortBuf[0x38]; +CPortClient::SendComplexAsyncRequest(&Port, 0x4000002F, args, flags); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker invokes an exported/com interface that eventually calls + CDwmAppHost::LpcNotifySettingsChange / LpcSyncFlush / + StartKernelRedirection. +2. Function allocates 56-byte _PORT_MESSAGE. +3. Control-code constant is written into Length fields. +4. NtAlpcSendWaitReceivePort executes; kernel trusts Length fields and + performs memcpy >56 bytes -> OOB read in kernel context. +5. Leaked kernel data can be re-used for privilege-escalation + primitives. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker sends specially crafted window / COM / +RPC traffic that causes DWM to run the affected LPC helper functions. +No admin rights are needed; exploitation happens within the user +session. + + +Patch Description +-------------------------------------------------------------------- +• Replaced all hand-rolled ALPC buffer construction with + CPortClient::SendSimple*/SendComplex* helpers that compute correct + DataLength / TotalLength values. +• Stopped persisting the process-heap handle inside the object; frees + always use GetProcessHeap(), eliminating the arbitrary-free helper. +• Added optional Wil feature gate and ALPC port-section cleanup in + Disconnect(). +• Tightened command-type masking in s_LpcCommandHandler. + + +Security Impact +-------------------------------------------------------------------- +Before the fix DWM could trick the kernel ALPC layer into reading an +attacker-controlled buffer past its bounds, leaking privileged memory +and providing a reliable local elevation-of-privilege primitive. The +heap-handle issue also opened a path to arbitrary free/DoS. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new helpers guarantee that _PORT_MESSAGE length fields match the +real allocation size, eliminating the OOB read avenue. Use of +GetProcessHeap() removes the untrusted pointer dereference. No new +observable pathways write over-large lengths, so the patch fully +addresses the documented root cause. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55681_dwmcore.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55681_dwmcore.dll.txt new file mode 100644 index 0000000..2e3c85b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55681_dwmcore.dll.txt @@ -0,0 +1,125 @@ +{'cve': 'CVE-2025-55681', 'confidence': 0.21, 'date': 1763407705.6438158, 'kb': 'KB5066835', 'change_count': 96, 'patch_store_uid': '9ea31a38-c5d1-473b-b782-3c02fef25a9c', 'file': 'dwmcore.dll'} +-------------------------------------------------------------------- +CVE-2025-55681 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Desktop Window Manager (dwmcore.dll) +Function: CBrushRenderingGraphBuilder::AddEffectBrush() +Windows 10/11 – user-mode service running inside the SYSTEM DWM +process. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-125: Out-of-Bounds Read +Secondary: CWE-822 Untrusted Pointer Dereference (possible write) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The routine builds an array named SubgraphOutput that contains one + 16-byte element for every compiled-effect sub-graph except the last + one: + count = CompiledEffect->GetNumSubGraphs(); + vector.resize(count-1); + The base pointer of this vector is kept in v16 (old) / v14 (new). + +2. While iterating through every effect input the code asks the + compiled-effect object for an "intermediate input index": + index = CompiledEffect->GetIntermediateIndex(g, i); + The returned *index* is stored in v26 (old) / v24 (new). + +3. Prior to the patch the index was used unchecked: + ptr = v16 + 16 * index; // pointer arithmetic + if (*(DWORD*)ptr != -1) // read outside vector when + // index >= vector.size() + CRenderingTechniqueFragment::AddIntermediateInput(...); + else { ... *(ptr+8) ... } // write outside vector + + If *index* is larger than (count-1) the calculation steps outside + the allocated buffer, producing an out-of-bounds read and, in the + else-path, a write of attacker-controlled data. The index value is + entirely controlled by the content of the compiled effect that is + sent from a lower-privileged client process to DWM. + +4. Because the pointer retrieved from the over-indexed location is + dereferenced later, arbitrary memory controlled by the attacker is + read (information disclosure) and may be written, enabling further + corruption and escalation to SYSTEM inside the DWM service. + +5. The issue is exploitable locally by any user that can create and + submit a malicious Composition effect graph. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if (v48) // intermediate input flag +{ + ptr = v16 + 16i64 * v26; // no bounds check + if (*(DWORD*)ptr != -1) + Fragment->AddIntermediateInput(*(DWORD*)ptr); + else { + tmp = *(QWORD*)(ptr + 8); // OOB read + *(QWORD*)(ptr + 8) = 0; // OOB write + ... // further dereference + } +} + +// AFTER +if (v42) // intermediate input flag +{ + if (FeatureEnabled && // new guard + v24 >= ((v54_high - v14) >> 4)) // index >= vector.size() + wil::FailFast(); // terminate + + ptr = v14 + 16 * v24; // safe + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client process creates a malicious Windows::UI::Composition effect + with a crafted intermediate-input index greater than the real + sub-graph count. +2. Effect is marshalled to the SYSTEM-level DWM process. +3. DWM calls CBrushRenderingGraphBuilder::AddEffectBrush(). +4. Function reads the attacker-controlled index and accesses the + SubgraphOutput vector out of bounds. +5. Memory beyond the buffer is read / written, enabling information + disclosure or further corruption leading to elevation of privilege. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Any process that can submit a composition +brush/effect (via UWP, WinUI, XAML, or DirectComposition APIs) can +provide the crafted effect data to DWM. + +Patch Description +-------------------------------------------------------------------- +• Introduced an explicit bounds check: + if (index >= vector.size()) FailFast(); + implemented through wil::Feature-gated check before dereferencing the + SubgraphOutput vector. +• Added synchronization and cleanup of any pending thread-pool work + before using the compiled effect. +• Re-organised local variables but no functional change beyond safety + check. + +Security Impact +-------------------------------------------------------------------- +Before the patch a low-privilege application could cause DWM to perform +out-of-bounds memory accesses inside its SYSTEM process context. The +read discloses memory and the paired write enables memory corruption +that can be turned into arbitrary code execution, ultimately allowing +local elevation of privilege (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The added index-range validation eliminates the OOB condition by +forcing a process-termination (FailFast) whenever a crafted index is +outside the allocated vector. This prevents both read and write past +the buffer and is therefore considered effective. No alternative code +paths remain that use the index without the same check, so the fix +appears complete. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55682_fvevol.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55682_fvevol.sys.txt new file mode 100644 index 0000000..95877c3 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55682_fvevol.sys.txt @@ -0,0 +1,132 @@ +{'cve': 'CVE-2025-55682', 'date': 1763402986.435249, 'confidence': 0.22, 'change_count': 6, 'patch_store_uid': 'efb29de7-ce28-4d45-a691-32e878d118b2', 'file': 'fvevol.sys', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-55682 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows BitLocker filter driver (fvevol.sys) – functions +FveFilterDeviceControl and FvePdcActivatorCallback. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper enforcement of behavioural workflow / state-machine error +(CWE-841). Practical manifestation: reference-counter underflow that +causes an unexpected release of the global BitLocker driver lock. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each BitLocker-protected volume owns a device structure (pointed to by +rdi/rbx in the decompilation, here referenced as DevCtx). Element +DevCtx[1133] is a signed 32-bit reference counter that tracks the +number of successful IOCTL_FVE_READ_WRITE_DEVICE_INIT (value +0x455610D8) requests that are currently active. As long as this +counter is non-zero the driver keeps the global BitLocker lock by +calling FveLockDriver(); when it reaches zero the lock is released via +FveUnlockDriver(). + +The complementary IOCTL 0x455610DC ( READ_WRITE_DEVICE_CLEANUP ) is +handled in FveFilterDeviceControl. In the vulnerable build the +sequence is: + +1. current = _InterlockedDecrement(&DevCtx[1133]); +2. if (current < 0) KeBugCheckEx(...); // *checked* builds only +3. FveReadWriteDeviceCleanup(); // performs bookkeeping +4. FveUnlockDriver(); // releases lock regardless of + // counter value + +On retail systems the bug-check is compiled out, therefore an attacker +can send CLEANUP without ever having sent the matching INIT. The +counter underflows (-1, ‑2 …) yet step 4 unconditionally calls +FveUnlockDriver(), leaving the global BitLocker lock open while the +counter is negative – a state that must never occur in the intended +workflow. + +Once the driver has been unlocked early, subsequent unauthorised I/O +(path traversal, raw sector writes, etc.) is accepted even though the +volume was never successfully initialised. This breaks BitLocker’s +security guarantee and enables a physical attacker to bypass +pre-boot-auth protection. + +The second function, FvePdcActivatorCallback, only received cosmetic +trace-id adjustments and does not participate in the defect itself. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before (IOCTL 0x455610DC branch) +_InterlockedDecrement((volatile signed __int32 *)v2 + 1133); +... +FveReadWriteDeviceCleanup(v2, 0i64); +FveUnlockDriver(v2); // always executed +``` + +```c +// After (simplified) +int new = _InterlockedDecrement((volatile signed __int32 *)v2 + 1133); +if (FeatureEnabled) +{ + if (new < 0) + WPP_SF_qd(...); // trace only – no unlock +} +else if (new < 0) +{ + KeBugCheckEx(...); // legacy path +} +... +if (!FeatureEnabled) // only when counter valid + FveUnlockDriver(v2); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens the device \\Device\\HarddiskVolumeXX\\FVE_vol. +2. Sends IOCTL 0x455610DC (READ_WRITE_DEVICE_CLEANUP) without the + prior 0x455610D8 INIT. +3. DevCtx[1133] decrements from 0 to -1. +4. In vulnerable code path FveUnlockDriver() is invoked. +5. BitLocker lock released; driver now processes unauthorised requests. + + +Attack Vector +-------------------------------------------------------------------- +Physical attacker (bootable media, DMA cable, or malicious bootloader) +who can talk to the fvevol device during early-boot or from WinPE can +issue the single crafted IOCTL to unlock the driver and access the +encrypted volume in clear. + + +Patch Description +-------------------------------------------------------------------- +1. Saves the result of InterlockedDecrement into a local variable. +2. Introduces a sanity check: + • If the counter became negative, FveUnlockDriver() is **not** + executed. + • In debug/feature builds a trace or bug-check is issued instead. +3. FveUnlockDriver() is now executed only when the reference count + remains non-negative, preserving the intended workflow. +4. Ancillary: WPP trace-id updates and minor refactorings; no security + impact. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could force an early unlock of the +BitLocker driver, defeating the encryption boundary and reading or +modifying disk sectors that should have remained protected. This is a +full security-feature bypass with physical access. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new logic blocks the unlock operation when the reference counter +underflows, thereby ensuring that the global lock can only be +released by a correctly paired INIT/CLEANUP sequence. State +invariants are preserved, preventing the bypass. No alternative path +in the patched binary was found that could still reach FveUnlockDriver +with a negative counter, so the fix is considered effective. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55685_printworkflowservice.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55685_printworkflowservice.dll.txt new file mode 100644 index 0000000..9b09a24 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55685_printworkflowservice.dll.txt @@ -0,0 +1,129 @@ +{'kb': 'KB5066835', 'cve': 'CVE-2025-55685', 'date': 1763403042.850828, 'change_count': 76, 'patch_store_uid': 'cfbf7ece-1a89-464d-80de-2d625e11c5f0', 'file': 'printworkflowservice.dll', 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-55685 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc – specifically the COM-based helper +classes found in printworkflowservice.dll: + • PrintSupportSettingsBroker + • PrintSupportVirtualPrinterSourceSession + • OpenWithViewModel shim wrappers produced by the C++/WinRT + code-generator. +These helpers are instantiated inside the PrintWorkflowUserSvc +service running as LocalSystem. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (dangling pointer returned from service +interfaces and later dereferenced after the backing object was +released). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Several automatically generated *produce<…>::get_*/Get* methods + simply returned a raw internal member pointer (often an + `hstring` or interface pointer) that belonged to the service + object itself. + Example (before patch): + v3 = a1 + 80; // member hstring inside broker + winrt::hstring::hstring(&v6,(hstring*)v3); // no AddRef – + // just alias + *a2 = v6; // return caller a direct reference + The same pattern existed in: + • GetAppInfo + • GetPrinterName + • GetTargetStorageFilePath +2. In the very large coroutine + PrintSupportSettingsBroker::LaunchSettingsUIForPrinterAsync + the broker pointer stored in the coroutine frame (*a1+449* in + the old build) was *not* reference-counted. As soon as the + async state machine suspended, other threads could release the + broker, leaving the frame with a dangling pointer that is later + dereferenced when the coroutine resumes (e.g. when it accesses + *(broker+168) or *(broker+64)). +3. Because PrintWorkflowUserSvc is running as SYSTEM and the + attacker only needs to be an authenticated local user able to + call the exposed broker interfaces, the dangling pointer can be + exploited to achieve elevation of privilege. The attacker + forces the broker object to be destroyed while the coroutine is + still live and then supplies controlled memory at the freed + address. +4. Absence of any strong lifetime management (AddRef/Release) or + deep copy when returning strings is therefore the root cause. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE – GetPrinterName (abridged) +winrt::hstring::hstring(&v6, (hstring*)(a1+80)); // alias internal +*a2 = v6; // return dangling +``` +```c +// AFTER – GetPrinterName (abridged) +PrinterName = PrintSupportSettingsBroker::GetPrinterName( + (a1-16)&-(a1!=0), &tmp); +*a2 = *PrinterName; // uses freshly copied hstring +*PrinterName = 0; // original zeroed, no aliasing +``` +```c +// BEFORE – coroutine stores raw pointer +*(_QWORD*)(a1+256) = a1-112; // no AddRef +``` +```c +// AFTER – coroutine takes a strong ref +*(_QWORD*)(a1+16) = broker; +if(broker) broker->AddRef(); // holds object alive +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client calls IPrintSupportSettingsBroker::LaunchSettingsUIFor­ + PrinterAsync(). +2. Service enters coroutine LaunchSettingsUIForPrinterAsync. +3. Coroutine stores raw broker pointer and suspends on an await. +4. Attacker triggers code path that releases the broker (e.g. + closing the related print session). +5. Coroutine resumes and dereferences the freed broker memory, + leading to UAF – typically write-what-where through vtbl member + access. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user creates a printing session and then uses +COM to talk to PrintWorkflowUserSvc. By carefully timing the life +cycle of the print session versus the coroutine await points, the +user frees the broker while it is still referenced inside the +service and gains code execution in the SYSTEM context. + +Patch Description +-------------------------------------------------------------------- +The February 2025 security update introduces comprehensive life- +cycle fixes: +1. All *produce* accessors now delegate to internal helper methods + that *copy* the requested hstring/path into a stack variable + before returning it – no more aliasing of internal members. +2. Coroutine frame now stores a *reference-counted* pointer to the + broker (`AddRef` on entry, `Release` in every exit path), + increasing the object size from 0x240 to 0x330 bytes. +3. Numerous new clean-up cases were added to ensure every early + exit releases its references, eliminating dangling pointers. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could cause PrintWorkflowUserSvc +(to which he already has access) to dereference freed memory and +execute arbitrary code as LocalSystem, thereby elevating privileges. +After patch, callers only receive independent copies, and the +service keeps strong references for the whole async duration, +removing the UAF condition. + +Fix Effectiveness +-------------------------------------------------------------------- +The ref-counting added to the coroutine plus the deep-copy helpers +eliminate all observable paths where a dangling pointer could be +exposed. Each exit path now releases the held reference exactly +once, preventing both leaks and double-frees. No residual alias of +internal members remains, so the fix is considered effective. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55686_printworkflowservice.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55686_printworkflowservice.dll.txt new file mode 100644 index 0000000..c5ce6de --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55686_printworkflowservice.dll.txt @@ -0,0 +1,122 @@ +{'cve': 'CVE-2025-55686', 'file': 'printworkflowservice.dll', 'patch_store_uid': 'cfbf7ece-1a89-464d-80de-2d625e11c5f0', 'date': 1763403040.8584487, 'confidence': 0.27, 'kb': 'KB5066835', 'change_count': 76} +-------------------------------------------------------------------- +CVE-2025-55686 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc – specifically the in-process COM +implementation inside printworkflowservice.dll that exposes the +PrintSupportSettingsBroker, PrintSupportSession and related helper +classes (LaunchSettingsUIForPrinterAsync, Get*PrintTicket, CloseView, +SetPrintSupportSettingsProcessId, GetTempFileForConversionAsync, …). + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Dangling-state access (CWE-416) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The broker keeps a single byte flag stored at +0x0D8 (shown in the +listing as *(_BYTE *)(object + 216)). The flag becomes TRUE only after +LaunchSettingsUIForPrinterAsync successfully marks the object as +"active" and allocates several subordinate resources: + • window-handles and modal-experience manager objects + • hstring buffers that hold the current / modified print ticket + • SRWLOCK-protected structures that reference the caller’s process + and temp files. + +Before the patch the following public methods never verified that the +object had reached the active state and therefore assumed that all +pointers were valid: + • GetCurrentPrintTicket() + • SetModifiedPrintTicket() + • SetPrintSupportSettingsProcessId() + • CloseView() + • PrintSupportSessionCommon::GetTempFileForConversionAsync() + +If an attacker invoked any of those APIs on a new +PrintSupportSettingsBroker instance *before* calling +LaunchSettingsUIForPrinterAsync, the methods would dereference members +that were still uninitialised (or had already been released after a +previous session) leading to use-after-free on: + – hstring at +0x070 (current ticket) + – window-manager IUnknowns at +0x038 / +0x020 + – SRWLOCK-protected ticket structures at +0x038 / +0x050 + – process/event handles at +0x098 etc. + +Because the service runs as LOCAL SERVICE while the attacker controls +the broker object through the UWP Print Support extension interface, a +UAF gives controlled pointers inside the privileged process. In +practice the attacker can replace the freed memory with a fake vtable +and achieve LOCAL SERVICE code-execution. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (no state check): +```c +// GetCurrentPrintTicket +winrt::hstring::hstring(a2, (const winrt::hstring*)(this + 0x70)); +``` +After (added validation): +```c +if (FeatureEnabled && !*((BYTE*)this + 0xD8)) + wil::details::in1diag3::Throw_Hr(..., 0x8000000E); +``` +Similar guard was added to SetModifiedPrintTicket, CloseView, +SetPrintSupportSettingsProcessId and several coroutine resumes. + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains IPrintSupportSettingsBroker COM pointer from the + service. +2. Calls GetCurrentPrintTicket() (or any other vulnerable method) + *without* first launching the settings UI. +3. Method dereferences uninitialised member; memory has been freed or + never allocated. +4. Service follows dangling pointer and executes attacker-supplied + memory → privilege escalation. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user owning a Print Support extension can talk to +PrintWorkflowUserSvc over the documented WinRT interfaces. No +additional privileges are required. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced a gating byte at offset +0xD8 that represents + "BrokerInitialised". LaunchSettingsUIForPrinterAsync now sets the + flag to 1 on success and clears it on cleanup. +2. Every externally reachable method starts with: + if (FeatureEnabled && !BrokerInitialised) + Throw_Hr(E_INVALIDARG /*0x8000000E*/) + This prevents any access to internal members when the object is in + an invalid lifecycle stage. +3. Added extensive HRESULT checking and size/overflow validation in + coroutine helpers and temp-file creation path. +4. Increased object size (from 0x240 to 0x330/0x3D0 bytes) to keep new + guard members and bookkeeping fields. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a malicious extension could achieve Local Elevation of +Privilege by triggering a use-after-free inside the service’s +high-integrity process. Successful exploitation yields code execution +in the PrintWorkflowUserSvc context (NT AUTHORITY\LOCAL SERVICE). + + +Fix Effectiveness +-------------------------------------------------------------------- +The added flag check is present in every entry point that previously +dereferenced broker fields. Any attempt to call them out of order now +results in an immediate, fail-fast HRESULT 0x8000000E, eliminating the +UAF. No remaining unguarded paths were observed in the supplied +patch; therefore the fix is technically sound. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55687_refsv1.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55687_refsv1.sys.txt new file mode 100644 index 0000000..98c4bf7 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55687_refsv1.sys.txt @@ -0,0 +1,126 @@ +{'file': 'refsv1.sys', 'cve': 'CVE-2025-55687', 'confidence': 0.25, 'change_count': 9, 'date': 1763408715.7397375, 'kb': 'KB5066835', 'patch_store_uid': 'cf676364-63cd-43e6-a72d-2bcd5824cf1e'} +-------------------------------------------------------------------- +CVE-2025-55687 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Resilient File System (ReFS) kernel driver +(refsv1.sys). The affected routines implement access checking, +security-descriptor query / modification and FCB creation. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race condition (improper synchronisation) +CWE-416: Use-after-free (dangling pointer to freed security +structure) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ReFS stores security information in a reference-counted structure +(REFS_SHARED_SECURITY, layout unknown; pointer held at FCB+0xB8 / +VCB+0xB8). Prior to the patch, many code paths retrieved that pointer +and used fields inside the underlying SECURITY_DESCRIPTOR without + 1. taking a reference (RefsReferenceSharedSecurity) and + 2. holding the protecting push-lock (offset +0xC0, same object) + +Consequently another thread could concurrently execute +RefsModifySecurity() and exchange the shared pointer, then +RefsDereferenceSharedSecurity() would free the old descriptor while it +was still being used by: + • RefsCanAdministerVolume() + • RefsNotifyAccessCheck() + • RefsQuerySecurity() + • AccessCheck() helper + • RefsCreateFcb() path that inserts a new FCB into the AVL table + +The windows above occur between the time the old code reads +FCB->SharedSecurity (or VCB->SharedSecurity) and the time SeAccessCheck +/ SeQuerySecurityDescriptorInfo dereferences the embedded SD at ++0x14. + +Typical sequence (simplified): + T0: thread A calls RefsCanAdministerVolume() + v9 = *(vcb->SharedSecurity) + T1: thread B calls RefsModifySecurity() and replaces the same field, + then dereferences the old object, dropping refcount to zero; + memory is freed. + T2: thread A continues execution, passes freed memory to + SeAccessCheck(), resulting in pool-use-after-free. Because the + data are interpreted as a SECURITY_DESCRIPTOR, the attacker can + craft fake memory and escalate privileges to SYSTEM. + +No refcount meant the window existed even on SMP systems with normal +I/O workloads; a local low-privileged user could reliably win the race +by spraying FCB security updates. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before (no lock / ref): +```c +v9 = *(struct _PRIVILEGE_SET **)(*(_QWORD *)(v8 + 8) + 8i64); // get +Control = v9[1].Control; // use it immediately +... +SeAccessCheck(a4, p_HighPart, 1u, Control, ...); +``` +After (take lock + ref): +```c +ExAcquirePushLockSharedEx(a3 + 192, 0i64); +v9 = *(_QWORD *)(a3 + 184); // SharedSecurity +v17 = v9; +RefsReferenceSharedSecurity(*(_QWORD *)(a3 + 88), v9); +ExReleasePushLockEx(a3 + 192, 0i64); +... +SeAccessCheck(...); +... +RefsDereferenceSharedSecurity(*(_QWORD *)(a3 + 88), &v17); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged process opens the same file/volume twice. +2. Thread A issues a read or user action causing + RefsCanAdministerVolume()/RefsNotifyAccessCheck()/AccessCheck(). +3. Thread B rapidly calls NtSetSecurityObject() on that file, + executing RefsModifySecurity() which swaps the shared security + pointer. +4. Thread B frees the old object when refcount reaches zero. +5. Thread A continues, dereferences dangling pointer, leading to pool + corruption / controlled SD evaluation. + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to open a ReFS file or volume and to +set its security descriptor. No additional privileges are required; +any authenticated user on the machine can trigger the race. + +Patch Description +-------------------------------------------------------------------- +1. Added explicit push-lock acquisition around every read or write of + the SharedSecurity pointer (shared for read, exclusive for write). +2. Inserted RefsReferenceSharedSecurity() immediately after obtaining + the pointer, and matching RefsDereferenceSharedSecurity() in all + exit paths. +3. RefsModifySecurity() now switches the pointer while holding the + lock, then releases it after the dereference of the old object. +4. RefsCreateFcb() enlarged the nonpaged FCB allocation to store a new + lock / pointer slot and initialised it to zero. +5. Minor housekeeping: new pool tag size, new magic number, updated + field offsets affected by the structural change. + +Security Impact +-------------------------------------------------------------------- +The race permitted a pool use-after-free in kernel context. An +attacker could craft a fake SECURITY_DESCRIPTOR in freed memory and +convince SeAccessCheck() to grant arbitrary access, leading to +privilege escalation to SYSTEM. Reliability is high because the race +is in hot code paths and the attacker controls timing. + +Fix Effectiveness +-------------------------------------------------------------------- +The added locking and reference counting correctly close the race +window for the analysed paths. All sites that previously touched +FCB/VCB->SharedSecurity now follow the new protocol. No remaining +unlock-on-error leaks were observed. Effectiveness appears complete +for the provided diff, but other undocumented call sites were not +reviewed (status: assumed adequate). diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55688_printworkflowservice.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55688_printworkflowservice.dll.txt new file mode 100644 index 0000000..1447da7 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55688_printworkflowservice.dll.txt @@ -0,0 +1,132 @@ +{'cve': 'CVE-2025-55688', 'kb': 'KB5066835', 'date': 1763406118.0938044, 'file': 'printworkflowservice.dll', 'confidence': 0.46, 'change_count': 76, 'patch_store_uid': 'cfbf7ece-1a89-464d-80de-2d625e11c5f0'} +-------------------------------------------------------------------- +CVE-2025-55688 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc (printworkflowservice.dll) +specifically the class +PrintSupportSessionCommon::DeleteTempFiles and related helper +functions in the Print Support settings broker. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (local privilege-escalation primitive). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +PrintSupportSessionCommon owns a collection of temporary files that +must be removed when a print job finishes. Two pointer-sized fields + at offset +0x10 and +0x18 ("start" and "end") were treated as a raw +contiguous C-array of winrt::Windows::Storage::IStorageFile +interfaces: + + start = *((QWORD*)this + 2); + end = *((QWORD*)this + 3); + +The pre-patch loop walked this memory with + + while (start != end) { DeleteAsync(start); start += 8; } + +DeleteAsync internally releases (DecRef) the IStorageFile object. If +that was the final reference the COM object is destroyed, freeing the +memory that the loop will dereference on the very next iteration. The +result is a classic use-after-free; the freed slab can be re-allocated +by attacker-controlled data before the second iteration, allowing an +arbitrary vtable pointer to be executed inside the PrintWorkflowUserSvc +process (SYSTEM integrity). + +Why the misuse occurs: + • The real container held at this+0x30 is an + IVectorView<IStorageFile>, *not* a flat array; its implementation + provides no stability or contiguity guarantees. + • Treating the collection as a raw buffer bypasses the reference + counting semantics that WinRT expects, so object lifetime is not + preserved across the loop. + • Once the first DeleteAsync drops the refcount to zero, the loop’s + cached pointer is stale but is nevertheless incremented and + dereferenced. + +A user running in the *caller* context can supply a crafted +IStorageFile implementation or arrange precise heap reuse to hijack +control-flow in the privileged service. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable loop (pre-patch) +void DeleteTempFiles(this) { + QWORD cur = *((QWORD*)this + 2); + QWORD end = *((QWORD*)this + 3); + while (cur != end) { + int opt = 1; + DeleteAsync(cur, &tmp, &opt); // releases *cur + IWindow::~IWindow(&tmp); + cur += 8; // uses freed memory + } +} + +// safe enumeration (post-patch) +if (FeatureEnabled) { + auto view = (IVectorView*) (this + 0x30); + UINT32 size = view->Size(); + for (UINT32 i = 0; i < size; ++i) { + ComPtr<IStorageFile> file; + view->GetAt(i, &file); // fresh reference each time + DeleteAsync(file.Get(), &tmp, &opt); + ... + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker starts a print job that causes temp files to be registered + with PrintSupportSessionCommon. +2. Service finishes the job and calls + PrintSupportSessionCommon::DeleteTempFiles. +3. First DeleteAsync frees the IStorageFile instance. +4. Loop increments raw pointer and dereferences freed memory. +5. Service executes attacker-controlled vtable, yielding SYSTEM code + execution. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running with normal user rights submits a +print job through the Print Workflow API and forces the service to +store attacker-controlled IStorageFile references. By reclaiming the +freed pool between iterations (e.g., via heap grooming) the attacker +redirects the next virtual call and elevates privileges. + +Patch Description +-------------------------------------------------------------------- +1. Added a feature flag check (Feature_2578215227) that selects a new + enumeration path. +2. The new path: + • Obtains the collection pointer stored at this+0x30 as + IVectorView. + • Queries Size() and iterates with GetAt(index) on every step, + obtaining a fresh, reference-counted interface each time. + • No longer relies on contiguous memory; object lifetime is correct. +3. Legacy contiguous-memory path is kept only when the feature flag is + disabled (not expected in production builds). +4. Defensive checks were also added to CloseView() to block illegal + re-entrance and to centralise error handling, but they are + secondary. + +Security Impact +-------------------------------------------------------------------- +The flaw allows a local user to execute arbitrary code in the context +of PrintWorkflowUserSvc (NT AUTHORITY\SYSTEM). Successful exploitation +provides full machine compromise and bypasses the Windows privilege +separation model. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code no longer dereferences stale pointers; each iteration +retrieves a fresh interface whose lifetime extends past the call to +DeleteAsync, eliminating the use-after-free window. Remaining risk: +if the new feature flag is forcibly disabled, the vulnerable branch is +reachable again. Provided the flag is permanently on for supported +builds, the fix is effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55689_printworkflowservice.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55689_printworkflowservice.dll.txt new file mode 100644 index 0000000..8f43094 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55689_printworkflowservice.dll.txt @@ -0,0 +1,130 @@ +{'change_count': 76, 'confidence': 0.2, 'kb': 'KB5066835', 'cve': 'CVE-2025-55689', 'date': 1763403021.6846497, 'file': 'printworkflowservice.dll', 'patch_store_uid': 'cfbf7ece-1a89-464d-80de-2d625e11c5f0'} +-------------------------------------------------------------------- +CVE-2025-55689 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc – specifically printworkflowservice.dll +implementations of +• PrintSupportSettingsBroker::GetCurrentPrintTicket +• PrintSupportSettingsBroker::IsCentennialApp +• PrintSupportSettingsBroker::CloseView +• PrintSupportSession::SessionStateHandler::SetSessionState + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free (CWE-416) caused by missing object-lifecycle/state checks +and illegal state transitions that lead to accesses of previously +released internal members. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several broker/session objects inside the PrintWorkflowUserSvc maintain +state bytes and pointers that reflect whether the object is still +valid. The critical flag is a single byte located at offset +0xD8 +(decimal 216) of PrintSupportSettingsBroker. When CloseView() is +invoked the object tears down its internal IFrameworkView pointer +(stored at +0x20) and sets the flag, but earlier builds allowed other +methods to be called afterwards: + +1. GetCurrentPrintTicket() copied an hstring that is located at +0x70 + (offset 112) without verifying that the broker was still active. If + the surrounding object had already been disposed the pointer inside + the hstring could have been freed, producing a dangling read and, in + the presence of attacker-controlled data, a write-what-where during + reference count manipulation. + +2. CloseView() itself could be executed twice. The second call would + dereference the freed IFrameworkView (*this+0x20) and call its + virtual Release() -> Use-after-free. + +3. IsCentennialApp() could lazily initialise application data after the + broker was marked inactive, again touching freed members. + +In addition, SessionStateHandler::SetSessionState accepted illegal +state transitions. An attacker could cause a second state update after +CloseView tore down the view. The routine accessed *parent+0x190 or +*parent+0x130 (background task, window, etc.) which could already be +released, extending the UAF surface beyond the broker object. + +Because all these calls can be triggered through the Print Support +WinRT interface by a normal user process, the bug yields local +Elevation of Privilege inside the PrintWorkflowUserSvc service account. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// OLD: GetCurrentPrintTicket – no state validation +winrt::hstring::hstring(a2, (const hstring *)(a1 + 112)); + +// NEW: added guard +if (FeatureEnabled && !*(BYTE *)(a1 + 216)) + wil::Throw_Hr(0x8000000E); // E_NOT_VALID_STATE + +// OLD: CloseView – double free possibility +if (*(QWORD *)this + 4) { + v3 = *((QWORD *)this + 3); // freed ptr + ... (*vtable)(v3, 88); // UAF +} + +// NEW: upfront guard prevents second invocation +if (FeatureEnabled && !*((BYTE *)this + 216)) + wil::Throw_Hr(0x8000000E); + +// OLD: SetSessionState – no transition check +if (a3 != 1 && (*a1 == 256 || ...)) { ... } +// NEW: transition must pass +if (IsStateTransitionAllowed(a1, a2, a3)) { ... } +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode sandboxed app communicates with the Print Support + broker via WinRT. +2. App closes the print-support view (CloseView) making the service + free internal objects. +3. App immediately calls either GetCurrentPrintTicket(), CloseView() + again, or forces a forbidden session-state transition. +4. The service dereferences internal pointers that now refer to freed + memory – attacker controls contents through heap grooming. +5. Crafted data placed in reclaimed memory allows arbitrary pointer + corruption and code execution in the PrintWorkflowUserSvc context. + +Attack Vector +-------------------------------------------------------------------- +Local – the attacker only needs the ability to load and call the public +WinRT PrintSupport APIs from a desktop or UWP application. No special +privileges are required. + +Patch Description +-------------------------------------------------------------------- +The update introduces three protective measures: +1. A per-object validity byte (offset +216) is now checked at the top + of GetCurrentPrintTicket(), CloseView() and IsCentennialApp(). When + the feature flag Feature_2578215227 is active and the object is not + in the valid state, the functions raise HRESULT 0x8000000E + (E_NOT_VALID_STATE) or 0x80070490 (ERROR_NOT_FOUND) instead of + accessing the freed memory. +2. CloseView() received new error-code locations (0x180/0x181) so the + first call still completes, while a second call fails fast. +3. SessionStateHandler::SetSessionState now calls + IsStateTransitionAllowed() before updating the state, blocking all + previously reachable illegal transitions that could operate on + destroyed parent structures. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could trigger a use-after-free and corrupt +heap structures in a privileged service, leading to local Elevation of +Privilege (EoP). Code execution would run under the PrintWorkflow +service SID, escaping the user context. + +Fix Effectiveness +-------------------------------------------------------------------- +The added state guard prevents any call into the broker after the +object is disposed, eliminating the dangling pointer dereferences. +Combined with stricter session-state validation the formerly reachable +UAF paths are closed. No residual code paths using the freed members +remain visible in the patched binary; therefore the fix appears +complete for the identified issue. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55690_printworkflowservice.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55690_printworkflowservice.dll.txt new file mode 100644 index 0000000..2c269d8 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55690_printworkflowservice.dll.txt @@ -0,0 +1,141 @@ +{'change_count': 76, 'file': 'printworkflowservice.dll', 'date': 1763404432.389121, 'patch_store_uid': 'cfbf7ece-1a89-464d-80de-2d625e11c5f0', 'kb': 'KB5066835', 'confidence': 0.21, 'cve': 'CVE-2025-55690'} +-------------------------------------------------------------------- +CVE-2025-55690 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc – specifically the WRL/WinRT *produce<> +adaptors inside printworkflowservice.dll that expose +IPrintSupport*Session* and related interfaces. + + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free (CWE-416) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each WinRT interface that the service returns to user-mode code is +backed by a C++/WinRT *produce<>* adaptor. Prior to the patch several +getters (e.g. GetAppInfo, GetTargetStorageFile, get_DocumentTitle, +get_SourceAppName, get_SourcePdlFormat, GetTargetStorageFilePath, …) +were implemented by + + 1. Fetching an internal implementation pointer stored inside the + session object: + v4 = *(_QWORD *)(this + offset); + + 2. Immediately dereferencing fields that live inside that + implementation object (e.g. +128, +136, +360, +144 bytes…). + + 3. Returning the raw pointer or handle to the caller, sometimes after + a shallow AddRef on only the field but **never** on the parent + implementation object that owns the field. + +Because the adaptor did **not** AddRef/Release the implementation +object itself, there was no lifetime guarantee between the moment the +pointer was read and the moment the field was dereferenced. If another +thread (or the same thread via re-entrancy) released the session object +in the small time-window, the memory could be freed and potentially re +allocated with attacker-controlled data. Any subsequent read or write +performed by the getter therefore operated on freed memory – a classic +use-after-free. The object that is freed is allocated inside the +PrintWorkflowUserSvc (SYSTEM integrity), so the dangling pointer is +still executed in a privileged context, giving an authorised local +attacker a path to privilege escalation. + +The most dangerous path is GetTargetStorageFile. The original code +reads a COM pointer at *(impl+360) and immediately calls +Microsoft::WRL::ComPtr<IInspectable>::InternalAddRef on it. If the +parent *impl* was already freed, the vtable of the allegedly valid +IInspectable object can be attacker-controlled, leading to arbitrary +code execution inside the service. + +Identical lifetime issues existed in the other property getters listed +above; they all followed the same pattern of +"read-pointer-without-AddRef, dereference, return". + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – GetTargetStorageFile (trimmed) + v4 = *(_QWORD *)v3; // grab impl pointer (no AddRef!) + v5 = *(_QWORD *)v3 + 360i64; // compute field address + if (!v4) v5 = 376i64; // static fallback + v8 = *(_QWORD *)v5; // read freed memory -> UAF + Microsoft::WRL::ComPtr<IInspectable>::InternalAddRef(&v8); +``` +```c +// after patch – same getter (trimmed) + if (FeatureEnabled) { + SessionInitDataHandler::GetTargetStorageFile(...,&v9); // helper + } else { + v7 = v4 + 376; // same field + v6 = *(_QWORD *)v7; + v9 = *(_QWORD *)v7; + InternalAddRef(&v9); // still needed but parent lives + } +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client obtains a session interface from PrintWorkflowUserSvc. +2. Client calls a getter (e.g. GetTargetStorageFile). +3. Inside the service, the getter executes while holding **no** reference + to the backing implementation object. +4. A second thread (or re-entrancy through COM callbacks) releases the + session object, freeing its heap block. +5. Original getter resumes, dereferences the stale pointer and executes + attacker-controlled memory. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. The attacker needs the ability to start a +PrintWorkflow session (any standard user can) and to race/free the +object before or during a property getter call. + + +Patch Description +-------------------------------------------------------------------- +All vulnerable getters were rewritten to eliminate direct field access. +The new code path: + +1. Converts the current *this* pointer to the owning implementation via + `(this - 16) & -(this != 0)`; this expression yields a valid base + pointer even for projected interfaces. +2. Immediately passes that pointer to a helper such as + PrintSupportSession::DocumentTitle or + SessionInitDataHandler::GetTargetStorageFile. These helpers create + and return **copies** (hstring clone or new COM reference) of the + requested data instead of exposing internal storage. +3. Any temporary COM object is properly AddRef'ed, released, and the + returned handle is zeroed before stack unwind, ensuring no dangling + pointer remains. +4. In two places the access path is additionally gated behind a WIL + feature check so that old layouts are avoided when the feature is + enabled. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, an authorised local attacker could achieve arbitrary +code execution in the PrintWorkflowUserSvc (runs as LocalSystem), +thereby elevating privileges to SYSTEM. The condition is reachable +from a low-integrity sandbox because the vulnerable service accepts +connections from any user account. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes all direct dereferences of potentially freed session +objects and delegates the work to helper routines that either hold a +references or return value copies whose lifetime is independent of the +original object. No remaining path in the modified code returns +internal pointers without first establishing ownership, so the original +use-after-free condition is closed. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55690_windows.graphics.printing.workflow.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55690_windows.graphics.printing.workflow.dll.txt new file mode 100644 index 0000000..38106b0 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55690_windows.graphics.printing.workflow.dll.txt @@ -0,0 +1,128 @@ +{'confidence': 0.11, 'change_count': 37, 'kb': 'KB5066835', 'cve': 'CVE-2025-55690', 'date': 1763404439.946167, 'patch_store_uid': 'c26aee8f-3ff1-4193-91b6-648f5f63ddec', 'file': 'windows.graphics.printing.workflow.dll'} +-------------------------------------------------------------------- +CVE-2025-55690 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc – function +PrintSupportUtil::IppDevObjectHelper::GetIppSoftwareDeviceObject +in windows.graphics.printing.workflow.dll. + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free (CWE-416). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper routine is in charge of translating a printer friendly +name into a DEV_OBJECT that represents the corresponding software +device (IPP or MCP queue). + +1. The function allocates an *array* of DEV_OBJECT pointers by + calling DevGetObjects(). The returned buffer (first element in + v32 before patch, v38 after patch) is registered for automatic + clean-up through the stack-allocated structure ‘v34/ v40’ whose + lifetime is controlled by the flag byte v35 (old) / v41 (new). + +2. On the success path the first DEV_OBJECT is copied for the + caller by + PrintCore::DevObjectHelpers::DevObjectCopy(v32, a1); + but **no reference is taken** – the copy is only a shallow + structure containing the same internal pointers as the original + enumeration buffer. + +3. Immediately afterwards the code executes the clean-up lambda + _lambda_8e6c..., which frees every DEV_OBJECT that still has its + ‘owning’ flag set. Because the flag is never cleared for the + element copied into the caller’s buffer, that object is freed + while the pointer stored in the output parameter ‘a1’ is still + returned to the caller. + +4. Subsequent use of the DEV_OBJECT by PrintWorkflowUserSvc + dereferences the dangling pointer, resulting in a use-after-free + in the service’s SYSTEM context. A local attacker able to make + the code path run (adding/connecting a printer, or by using a + crafted per-user printer name) can exploit the dangling pointer + to gain arbitrary code execution in the service and therefore + elevate privileges. + +Affected structures / parameters + • v32 – first DEV_OBJECT returned by DevGetObjects + • v34 – stack array managing auto-free of the buffer (flag v35) + • a1 – OUTPUT DEV_OBJECT supplied by the caller + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch +PrintCore::DevObjectHelpers::DevObjectCopy(v32, a1); // shallow copy +v35 = 0; // <== does NOT + // clear per-item +_lambda_...::operator()(v34); // frees v32 + +// After patch +PrintCore::DevObjectHelpers::DevObjectCopy(v38, a1); // same call +v41 = 0; // flag cleared +// but entry for the selected object is also removed from +// the auto-free list, so the following clean-up does not +// touch it. +_lambda_...::operator()(v40); +``` +(The patch also replaces the property-list initialiser value 0x2 with +0x20002 (131074) so DevGetObjects now returns an *independent* object +instance.) +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User (low priv) installs or connects to a per-user printer. +2. PrintWorkflowUserSvc receives the request and calls + IppDevObjectHelper::GetIppSoftwareDeviceObject. +3. Function returns dangling DEV_OBJECT pointer as described. +4. Service performs further operations on the freed object – UAF. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to add or rename a printer so that +PrintWorkflowUserSvc is forced through the IPP branch can trigger the +flaw. No additional privileges are required beyond the default ones +needed to create a per-user printer connection. + + +Patch Description +-------------------------------------------------------------------- +1. Introduces a feature-flag gate so the dangerous path is taken only + when Feature_IppPsaForEnterprise is enabled. +2. Corrects property initialisers passed to DevGetObjects (switch + from 0x0002 to 0x20002) so a fully independent object is + allocated. +3. Ensures the element copied to the caller is *removed* from the + auto-free list before the clean-up lambda executes (flag handling + re-worked around v41 / v40). +4. Adds stronger bounds-checking (v33 > 1) and tidies several + uninitialised variables. + + +Security Impact +-------------------------------------------------------------------- +The dangling DEV_OBJECT pointer can be dereferenced with attacker +controlled timing and content. Because the service runs as SYSTEM on +Windows, successful exploitation yields local elevation of privilege +in the kernel print workflow component. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch severs the lifetime coupling between the caller’s DEV_OBJECT +and the temporary enumeration buffer by (a) producing an independent +copy and (b) preventing the clean-up routine from touching the object +handed back to the caller. No code path returns a pointer that is +later released in the same function, so the original UAF is removed. +No alternative dangling references were observed in the modified +routine. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55691_printworkflowservice.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55691_printworkflowservice.dll.txt new file mode 100644 index 0000000..60e744b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55691_printworkflowservice.dll.txt @@ -0,0 +1,129 @@ +{'file': 'printworkflowservice.dll', 'date': 1763403015.0499089, 'cve': 'CVE-2025-55691', 'kb': 'KB5066835', 'change_count': 76, 'confidence': 0.18, 'patch_store_uid': 'cfbf7ece-1a89-464d-80de-2d625e11c5f0'} +-------------------------------------------------------------------- +CVE-2025-55691 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PrintWorkflowUserSvc (printworkflowservice.dll) – +implementation of the internal COM/WinRT interface +Windows::Graphics::Internal::Printing::PrintSupport:: +IPrintSupportSettingsBroker. + +Vulnerability Class +-------------------------------------------------------------------- +Use-after-free (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Two broker helper methods were exported through the WinRT +implementation class + winrt::impl::produce<... + PrintSupportSettingsBroker, + IPrintSupportSettingsBroker>:: + GetAppInfo() + GetPrinterName() +Each received a "this" pointer (a1) and an out-parameter pointer (a2) +intended to receive either an IWindow* (app info) or an HSTRING +(printer name). + +Old flow (simplified): +1. Initialise *a2 = 0. +2. Construct a temporary stack object + __int64 v6; // holds the return object +3. Call a helper that populates v6. +4. Copy v6 into the caller-supplied *a2. +5. Zero v6 and immediately invoke the appropriate destructor: + IWindow::~IWindow(&v6) or + handle_type<>::close(&v6) +6. Return 0. + +Because ownership of the returned object was never transferred, step 5 +frees the memory that *a2 continues to reference. Any subsequent use +of *a2 by the caller operates on freed memory, forming a classic +use-after-free. The defect is in the object-lifetime contract: the +implementation assumed a move-semantics copy, but instead performed a +shallow pointer copy followed by destruction of the original holder. + +The problem affects: + • IWindow objects obtained via + AppInfoHelper::GetAppInfoFromHwnd() + • HSTRING buffers obtained via winrt::hstring::hstring() +Both structures contain internal vtables/buffers that, once freed, may +be re-occupied by attacker-controlled data leading to controlled code +execution when later dereferenced by high-privilege code. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Vulnerable pattern (before patch) +*vOut = 0; +AppInfoHelper::GetAppInfoFromHwnd(&v6, hwnd); +__int64 tmp = v6; // shallow copy +v6 = 0; // detach pointer +*vOut = tmp; // caller now holds dangling pointer +IWindow::~IWindow(&v6); // frees object just copied + +// Patched pattern +*vOut = 0; +AppInfo = PrintSupportSettingsBroker::GetAppInfo(thisAdj, &v6); +__int64 tmp = *AppInfo; +*AppInfo = 0; // transfer ownership safely +*vOut = tmp; +IWindow::~IWindow(&v6); // now operates on null object +``` +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege client obtains a proxy to the privileged + IPrintSupportSettingsBroker interface exposed by + PrintWorkflowUserSvc. +2. Client invokes GetAppInfo() or GetPrinterName(). +3. Service executes vulnerable code path, freeing the object while the + pointer is still returned to the client. +4. Client heap-sprays / re-allocates memory at the freed address. +5. Subsequent service-side use of the stale pointer (e.g., reference + count manipulation, vtable call, string access) results in + controlled memory corruption, permitting privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Any user able to communicate with +PrintWorkflowUserSvc through the COM/WinRT broker interface can supply +crafted calls and heap layout to exploit the dangling pointer. + +Patch Description +-------------------------------------------------------------------- +The patch changes both methods to: +1. Delegate the actual data retrieval to static helper methods that + allocate the return value and return its address. +2. After the helper returns, copy the pointer value out and NULL the + helper-owned pointer (*AppInfo = 0 or *PrinterName = 0). +3. Only then invoke the destructor on the temporary holder, which now + contains a null pointer, preventing accidental free of the object + passed to the caller. + +In effect, explicit ownership transfer is performed before any +cleanup, ensuring the object’s lifetime extends beyond the function +scope. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, the service could return dangling IWindow or +HSTRING pointers to itself or other high-privilege consumers. An +attacker able to influence the heap layout could turn the use-after- +free into arbitrary code execution in the PrintWorkflowUserSvc +context, elevating to SERVICE or SYSTEM privileges. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code pattern eliminates the double-free/dangling-pointer +condition by: + • Returning the address of an allocated object instead of the object + itself. + • Nulling the original owner before destruction, preventing the free. +A review shows that no further destructor executes on the pointer now +held by the caller, so lifetime is preserved. Therefore, the fix is +considered effective for these two call sites; however, other methods +should be audited for the same pattern. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55692_wer.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55692_wer.dll.txt new file mode 100644 index 0000000..914881d --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55692_wer.dll.txt @@ -0,0 +1,137 @@ +{'confidence': 0.22, 'date': 1763403012.2374687, 'kb': 'KB5066835', 'cve': 'CVE-2025-55692', 'file': 'wer.dll', 'patch_store_uid': 'a41eb3d2-76ed-4075-b075-6a9f72571101', 'change_count': 28} +-------------------------------------------------------------------- +CVE-2025-55692 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Error Reporting (WER) – wer.dll, routine AeWERQueryFileInfo() + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation (CWE-20) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AeWERQueryFileInfo() is invoked by the WER service (running as +NT AUTHORITY\SYSTEM) to obtain ProgramId and FileId strings from an +Amcache hive the service loads with RegLoadAppKeyW(). These two values +are expected to be fixed-length GUID strings (44 WCHARs including the +terminator). + +In the vulnerable build the code path protected by +Feature_FileInfo_Improved_Error_Checking() is *optional*. If the +feature flag is disabled (the default on released builds) the function +accepts *any* non-empty ProgramId or FileId value: + + else if ( *pvData || *v15 ) + v6 = 0; // report success + +No verification of length or formatting is performed. Immediately +afterwards upper-layer logic trusts the returned buffer as a valid GUID +and uses it to build registry paths and file names while keeping the +fixed 256-byte stack allocation that was sized for a 44 WCHAR string. + +An attacker controlling the Amcache hive can therefore place an overly +long (or unterminated) Unicode string in either ProgramId or FileId. +When the service later scans the buffer for the terminating NUL it runs +past the 256-byte stack slot and reads arbitrary stack memory. That +stack memory is subsequently reused to build paths, enabling a chosen +stack-data overwrite of wide-string path components. By crafting the +string so that the resulting path resolves to a file or key under the +attacker’s control, arbitrary-file-write and elevation of privilege can +be achieved. + +Key data structures / parameters affected: + a1 + 16 – 256-byte stack buffer for ProgramId + a1 + 272 – 256-byte stack buffer for FileId + pcbData[0] – size supplied to RegGetValueW (256) + pvData – pointer to destination buffer + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable branch (before patch) +pcbData[0] = 256; +ValueW = RegGetValueW(hkey, SubKey, L"ProgramId", 2u, + 0, (PVOID)(a1 + 16), pcbData); +... +else if ( *pvData || *v15 ) // only one value needs to be non-empty +{ + v6 = 0; // success without length check +} +``` +```c +// fixed branch (after patch) +pcbData = 256; +ValueW = RegGetValueW(hkey, SubKey, L"ProgramId", 2u, + 0, (PVOID)(a1 + 16), &pcbData); +... +// unconditional strict length verification +v20 = -1; v21 = -1; +while ( *((WORD*)pvData + ++v21) ); // FileId length +... +if ( v21 != 44 ) goto fail; +while ( *(_WORD*)(v16 + 2*++v20) ); // ProgramId length +if ( v20 == 44 ) v6 = 0; else v6 = ERROR_FILE_NOT_FOUND; +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker prepares malicious Amcache.hve containing overlong + ProgramId / FileId values. +2. WER service calls AeWERQueryFileInfo() while processing a crafted + file path. +3. RegGetValueW() copies the attacker-controlled string into the fixed + 256-byte stack buffer. +4. Because the feature flag is disabled, size checks are skipped and + the function reports success. +5. Subsequent string operations scan past the end of the buffer, + corrupting stack data that is later used to compose system file / + registry paths. +6. Crafted corruption causes the service to create or open objects in a + location writable by the attacker, yielding SYSTEM-level file or + registry writes and privilege escalation. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker supplies a malicious Amcache.hve under +%ProgramData%\Microsoft\AppCompat\Programs (or any redirected +persisted-location identified by AeGetPersistedLocation). No +administrative privileges are required; standard user write permission +is sufficient. + + +Patch Description +-------------------------------------------------------------------- +1. Feature gating removed – improved checks now always executed. +2. Length of both ProgramId and FileId is verified to be exactly 44 + WCHARs; otherwise ERROR_FILE_NOT_FOUND is returned. +3. pcbData changed from two-element array to a single DWORD, preventing + unintended size confusion. +4. Error logging consolidated; any validation failure now unwinds with + a consistent error path. +5. Several variables renamed to emphasise correct types (pvData becomes + void*, etc.). + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a standard user could coerce the SYSTEM-level WER +service into writing arbitrary files / registry keys or otherwise +corrupting its own execution flow, resulting in elevation of privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +The unconditional length check eliminates the ability to inject +overlong or unterminated strings into the fixed-size stack buffers. +Combined with the corrected pcbData handling and unified error paths, +no observable path remains to overflow or misuse the buffers. The +patch therefore fully mitigates the described vulnerability. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55693_wdf01000.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55693_wdf01000.sys.txt new file mode 100644 index 0000000..17ac115 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55693_wdf01000.sys.txt @@ -0,0 +1,143 @@ +{'confidence': 0.2, 'date': 1763406121.5159028, 'cve': 'CVE-2025-55693', 'patch_store_uid': 'c0400ba2-c5be-4f2a-9ee4-a78c0603d864', 'change_count': 3, 'kb': 'KB5066835', 'file': 'wdf01000.sys'} +-------------------------------------------------------------------- +CVE-2025-55693 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Driver Frameworks (WDF) – kernel-mode library wdf01000.sys. +The vulnerable code lies in the DMA common buffer lifetime +management routines (FxCommonBuffer / FxDmaEnabler classes). + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free (dangling object reference) – CWE-416. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +FxCommonBuffer objects represent DMA common buffers that may be +exposed to user-mode drivers through WDF handles. When such an +object is destroyed the framework must + 1. notify the owning user-mode driver via FxObject::CallCleanup() + 2. release the physical/common buffer memory with + FxCommonBuffer::FreeCommonBuffer(). + +In the original implementation of + FxCommonBuffer::Dispose() +the function executed only step 2: + FreeCommonBuffer(this); + return 1; + +For buffers created by a user-mode driver the associated handle +remained valid inside the driver process because CallCleanup() was +never issued. The handle therefore pointed to memory that had just +been freed by the kernel, producing a classic dangling pointer. Any +subsequent use of the handle (IOCTLs, WdfObject* API, etc.) caused +the framework to dereference freed memory inside the kernel address +space, permitting memory corruption and potential elevation of +privilege. + +Whether a buffer was owned by a user-mode driver was not tracked +explicitly. Consequently Dispose() could not decide when a cleanup +callback was required, leading to the unconditional premature free. + +Structures / fields involved: + • FxCommonBuffer – victim object being freed. + • FxDmaEnabler – new boolean field m_RunningUserModeDriver marks + that the associated driver runs in user mode. + • FxObject::CallCleanup() – responsible for severing all outstanding + references/handles. + +Sequence that leads to UAF: + 1. UMDF driver creates DMA common buffer (WdfDmaEnabler... + CreateCommonBuffer) – m_RunningUserModeDriver should be 1. + 2. Driver (or framework) triggers object dispose path. + 3. Old Dispose() frees memory without calling cleanup – handle in + user space still lives. + 4. Driver later touches the handle – framework accesses freed + kernel memory -> Use After Free. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +unsigned __int8 __fastcall FxCommonBuffer::Dispose(FxCommonBuffer *this) +{ + FxCommonBuffer::FreeCommonBuffer(this); + return 1; // cleanup never performed +} + +// AFTER (patched) +char __fastcall FxCommonBuffer::Dispose(FxCommonBuffer *this) +{ + char ok = 0; + if (Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage() && + this->m_DmaEnabler->m_RunningUserModeDriver) + FxObject::CallCleanup(this); // notify & drop handles + else + ok = 1; + FxCommonBuffer::FreeCommonBuffer(this); + return ok; +} + +// Support code – flag initialisation +FxDmaEnabler::FxDmaEnabler() +{ + ... + if (Feature_IsEnabled()) + this->m_RunningUserModeDriver = 0; // default +} + +FxDmaEnabler::Initialize(...) +{ + ... + // When a user-mode driver is detected + this->m_RunningUserModeDriver = 1; + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode driver creates a DMA enabler and common buffer. +2. Framework later calls FxCommonBuffer::Dispose() while driver still + keeps the WDF handle. +3. Old code frees memory; handle remains valid in user context. +4. Any subsequent driver action that touches the handle + (WdfObjectDelete, IOCTL, etc.) dereferences freed kernel memory – + leading to UAF-based memory corruption. + +Attack Vector +-------------------------------------------------------------------- +Local attacker able to load or hijack a user-mode WDF driver can +craft sequences that cause early disposal of a common buffer while +retaining its handle, then issue operations on the stale handle to +achieve arbitrary kernel memory read/write, thereby escalating +privileges. + +Patch Description +-------------------------------------------------------------------- +1. Added a per-enabler boolean member m_RunningUserModeDriver. +2. Constructor sets the field to 0; Initialize() sets it to 1 when a + user-mode driver context is detected (Config->Flags & 4). +3. FxCommonBuffer::Dispose() now checks both the feature gate and + m_RunningUserModeDriver. When both are true it invokes + FxObject::CallCleanup() before freeing the buffer, ensuring all + outstanding user-mode references are cleared. +4. Return value changed to reflect whether cleanup was required. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a local attacker could obtain a dangling WDF handle +into freed kernel memory, enabling arbitrary memory corruption in the +kernel. Successful exploitation results in elevation of privilege +from ordinary user to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The added flag tracks user-mode ownership, and Dispose() calls the +mandatory cleanup routine before freeing the object. This removes +all kernel and user references, so later accesses cannot reach freed +memory, effectively closing the use-after-free condition. No other +paths free the buffer without cleanup; therefore the patch is +expected to fully remediate the vulnerability. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55696_ntdll.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55696_ntdll.dll.txt new file mode 100644 index 0000000..275bc19 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55696_ntdll.dll.txt @@ -0,0 +1,147 @@ +{'change_count': 38, 'confidence': 0.19, 'patch_store_uid': 'b14e8e60-c1b7-44d3-9673-95c544b337a0', 'kb': 'KB5066835', 'date': 1763407777.2180753, 'file': 'ntdll.dll', 'cve': 'CVE-2025-55696'} +-------------------------------------------------------------------- +CVE-2025-55696 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +ntdll!RtlVirtualUnwind, ntdll!RtlVirtualUnwind2 and +ntdll!RtlpUnwindPrologue – user-mode x64 unwinder helpers that parse +and manipulate CONTEXT/XSTATE records while walking a thread’s call +stack. + + +Vulnerability Class +-------------------------------------------------------------------- +Untrusted pointer / structure dereference leading to out-of-bounds +read-write (aka memory corruption). Closest CWEs: CWE-822 and +CWE-787. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The three routines consume a caller-supplied CONTEXT structure. If +the CONTEXT advertises extended state (bit 0x40 – +CONTEXT_XSTATE/CONTEXT_EXCEPTION_ACTIVE) the code expects a second +embedded header (CONTEXT[1]) to describe the size and offset of the +XSTATE area: + P1Home – size of the legacy CONTEXT + P3Home – size of the XSTATE data + P2Home.hi – supposed to be 1232 (sizeof(CONTEXT)) + P2Home.lo – byte offset from CONTEXT to the XSTATE buffer + +Before the patch the functions tried to validate the caller’s +ContextFlags with hand-rolled bit-tests and a few range checks. The +logic contained several gaps: +1. A single failing test immediately fell through to normal + processing, so many illegal flag combinations were accepted. +2. Negative return paths were not taken for STATUS_INVALID_PARAMETER + (0xC000000D, value ‑1073741811). This allowed a crafted set of + flags to bypass the check entirely. +3. If the (partial) check passed, the code used the attacker-controlled + P2Home offset to compute pointers such as + (char *)&Context[1] + P2Home + and later wrote register values and shadow-stack data through those + pointers without further bounds checking. A tiny offset or integer + underflow therefore redirected the write outside the allocated + buffer, corrupting adjacent memory. +4. RtlpUnwindPrologue repeated the same pattern when it tried to locate + the Extended Feature 0x0B (machine-frame continuation) inside the + XSTATE area; the location was calculated manually and trusted. + +Because all three helpers run inside ntdll (mapped at fixed addresses +in every process) an unprivileged process could craft a CONTEXT/XSTATE +pair and call RtlVirtualUnwind* (directly or indirectly via C++ EH or +unwind-helper APIs). When the unwinder executed it would overwrite +controlled process memory, enabling arbitrary code execution in the +context of the current process and, through subsequent attacks against +system services, local privilege escalation. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (RtlVirtualUnwind) +if ((ContextFlags & 0x100040) == 0x100040) { + P1Home = Context[1].P1Home; + P3Home = Context[1].P3Home; + ... + if ( ... || + (struct _CONTEXT *)((char *)&Context[1] + + SLODWORD(Context[1].P2Home)) != Context ) { + Context->ContextFlags &= 0xFFFFFFBF; // but pointer already + // dereferenced above + } +} +``` +```c +// post-patch +status = RtlpValidateContextFlags(Context->ContextFlags, 0); +if (status < 0) { + Context->ContextFlags = 0x10000B; // sanitize and bail + ... +} +... +status_ok: +if ((Context->ContextFlags & 0x100040) == 0x100040) { + ... + if (bad_range) + Context->ContextFlags &= ~0x40; // mask XSTATE, *before* use +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker builds fake CONTEXT with: + • ContextFlags = 0x100040 (CONTROL|XSTATE) + • Context[1].P2Home = small/negative offset + • Context[1].{P1Home,P3Home} adjusted to satisfy minimal range test +2. Attacker calls any API that eventually executes RtlVirtualUnwind* + while processing the crafted CONTEXT. +3. Manual validation accepts the flags path; pointer arithmetic yields + address outside the buffer. +4. Subsequent unwinder write corrupts memory → arbitrary code exec. + + +Attack Vector +-------------------------------------------------------------------- +Local. Any user-mode code able to supply a forged CONTEXT/XSTATE to an +unwind helper (e.g. through RtlUnwindEx, RaiseException with hijacked +stack, or custom SEH frames) can hit the vulnerable path. No special +privileges are required. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced common helper RtlpValidateContextFlags() and replaced all + ad-hoc flag screens with this routine. +2. Treats STATUS_INVALID_PARAMETER (-1073741811) as fatal – caller’s + CONTEXT is forcibly converted to a minimal safe set (0x10000B). +3. When the XSTATE bit is present additional size/offset checks are + preserved but now executed *before* any pointer use; failing them + clears bit 0x40 so extended state is ignored. +4. RtlpUnwindPrologue now calls RtlLocateExtendedFeature() instead of + recomputing the pointer by hand and updates machine-frame bookkeeping + only if the located feature header is present and marked valid. +5. Multiple index variables were changed from signed to unsigned to + remove wrap-around corner cases. + + +Security Impact +-------------------------------------------------------------------- +Pre-patch an attacker could achieve memory corruption in ntdll’s data +segment or on the application stack, leading to process compromise and +potential elevation of privilege via crafted CONTEXT records. Post +patch the CONTEXT is strictly validated and invalid combinations are +rejected or neutered, closing the write-what-where primitive. + + +Fix Effectiveness +-------------------------------------------------------------------- +The centralisation of validation logic and elimination of manual +pointer maths comprehensively blocks the previously exploitable paths. +No remaining untrusted dereferences were observed in the patched code. +A negative status from RtlpValidateContextFlags halts further +processing, preventing misuse even if new flag combinations emerge. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55698_dxgkrnl.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55698_dxgkrnl.sys.txt new file mode 100644 index 0000000..4c0bf79 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55698_dxgkrnl.sys.txt @@ -0,0 +1,129 @@ +{'confidence': 0.12, 'kb': 'KB5066835', 'date': 1763407807.2532375, 'patch_store_uid': 'afe9fa6e-0a1c-4325-b44e-2c4e6256a6dd', 'cve': 'CVE-2025-55698', 'file': 'dxgkrnl.sys', 'change_count': 230} +-------------------------------------------------------------------- +CVE-2025-55698 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – dxgkrnl.sys (DirectX graphics kernel). Faulty +routine: ComputeQueryInterface() which supplies a procedure table to +user-mode display drivers. + +Vulnerability Class +-------------------------------------------------------------------- +NULL-pointer dereference (CWE-476) leading to a kernel-mode denial of +service. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +User-mode display drivers obtain a table of kernel callbacks by +invoking DxgkComputeEscape → ComputeQueryInterface() and passing a +caller-allocated _DXGKCOMPUTE_INTERFACE structure. Two 16-bit header +fields inside that structure describe + • Size (_In_ WORD InterfaceSize) + • Version(_In_ WORD InterfaceVersion) + +The pre-patch implementation tried to validate the pair but the logic +was wrong: + +1. When Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_25() was + enabled the code entered a nested if/else chain. +2. For InterfaceVersion == 3 the function only compared Size against + 624 (expected for v3). For InterfaceVersion == 2 it compared Size + against 616. That was correct – but the error path + (labelled “LABEL_7”) *never reset the Version field check*. +3. A crafted structure could therefore reach the success path when + InterfaceVersion == 3 && Size == 616 + or + InterfaceVersion == 2 && Size == 624 + – clearly inconsistent combinations. +4. After the superficial checks the routine unconditionally filled 78 + QWORD slots beginning at a1+8 with function pointers. For version + 2 only 76 entries are valid; entries 76-77 were left untouched and + therefore remained NULL. +5. Because the routine also returned the *larger* size (624) to the + caller, user mode trusted the table to hold 78 valid callbacks. A + subsequent call into either of the two missing callbacks + (offsets 76 or 77) made the kernel dereference a NULL function + pointer and bug-check (typically 0x3B or 0x50). + +Attackers only need DEVICE access – the call is reachable from a +sandboxed, low-integrity process and no additional privileges are +required. The exception is raised in kernel context, resulting in an +immediate system crash (DoS). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (simplified) +if (FeatureEnabled()) { + WORD ver = a1->Version; + WORD size = a1->Size; + if (ver == 3) valid = (size == 624); + else if (ver == 2) valid = (size == 616); + if (!valid) goto LABEL_7; // wrong path keeps ver/size coupled +} +... +// fill 78 callbacks no matter which version +*((QWORD*)a1+76) = ...; +*((QWORD*)a1+77) = FeatureEnabled() && ver==3 ? DxgkQueryStatisticsK + : NULL; // stays NULL +*a2 = size; // returns 624 even for ver==2 (incorrect) +``` + +```c +// post-patch +WORD ver = a1->Version; +WORD size = a1->Size; +if (ver == 3) { + if (size != 624) goto fail; +} else if (ver == 2) { + if (size != 616) goto fail; +} else { + goto fail; +} +// now safe – build table; slot 77 only filled for ver==3 +``` +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker crafts _DXGKCOMPUTE_INTERFACE with Version = 2 and Size = + 624. +2. Escape ioctl → DxgkComputeEscape → ComputeQueryInterface. +3. Mis-validation returns success; table pointer handed back. +4. Attacker calls callback #77 (index 0x4D) → NULL execution → kernel + bug-check. + +Attack Vector +-------------------------------------------------------------------- +Any authenticated local user that can open a DirectX device (e.g. +D3DKMTCreateDevice) can submit the malformed interface structure. No +admin rights or graphics hardware access is required; the issue is +fully local. + +Patch Description +-------------------------------------------------------------------- +The update completely rewrote the size/version gate: +• Removed the convoluted LABEL_7 error path. +• Performs a strict cross-check: + (Version==3 && Size==624) OR (Version==2 && Size==616) +• Any other combination returns STATUS_INVALID_PARAMETER. +• The returned size (*a2) is now taken from the validated pair, not + from a mutable local variable. +• The pointer to DxgkQueryStatisticsK (slot 77) is only assigned when + Version==3 – older interfaces no longer expose a NULL entry. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a non-privileged process could reliably crash the +system, leading to a total denial of service. There is no information +leak or privilege escalation; availability is the sole impact. + +Fix Effectiveness +-------------------------------------------------------------------- +The revised check guarantees that an interface’s Size matches the +expected layout for its Version, preventing the creation of partially +initialised procedure tables. Attempting to pass a mismatched pair +now returns STATUS_INVALID_PARAMETER before any callback table is +exposed, eliminating the NULL dereference vector. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55699_ntoskrnl.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55699_ntoskrnl.exe.txt new file mode 100644 index 0000000..fce3a65 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55699_ntoskrnl.exe.txt @@ -0,0 +1,122 @@ +{'file': 'ntoskrnl.exe', 'kb': 'KB5066835', 'cve': 'CVE-2025-55699', 'patch_store_uid': 'c1b97b38-6328-4043-8cf8-12e6eafee863', 'date': 1763407861.858766, 'confidence': 0.25, 'change_count': 222} +-------------------------------------------------------------------- +CVE-2025-55699 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel – Plug-and-Play subsystem (ntoskrnl.exe) +Function: PnpValidatePropertyData, responsible for validating user +supplied registry-style data that is passed in PnP property IOCTLs. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds read leading to kernel information disclosure. +(CWE-125 / results in CWE-200) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. PnpValidatePropertyData receives: + • psz – pointer to caller-supplied buffer that contains a value of + type REG_SZ / REG_MULTI_SZ / REG_EXPAND_SZ, etc. + • SecurityDescriptorLength (cb) – length of the buffer in bytes. + • a3 – property type/flags (upper nibble 0x2000 == string types). + +2. For string types the original code entered the branch beginning at + label 17: + while ( *(_WORD*)psz ) // ❶ dereference first WORD + { + /* calculate remaining size */ + /* scan for terminating NUL */ + /* advance psz past the NUL */ + } + +3. The loop advanced the psz pointer blindly by the computed length + plus two bytes and then immediately re-evaluated `*(_WORD*)psz`. + If the last string inside the user buffer was missing the required + trailing NUL (or was only partially present), psz became equal to + cb (end-of-buffer). The very next iteration dereferenced + *(_WORD*)psz (❶) which now lies beyond the caller-controlled + allocation. + +4. Because the access is a kernel read, the function leaks the first + 16-bit value that happens to reside past the end of the buffer. By + supplying a length that ends exactly at the boundary an attacker can + read arbitrary adjacent kernel memory until a zero word is hit. + +5. The function later copies, returns, or otherwise makes decisions + based on the over-read data, enabling an authorised local attacker + to disclose uninitialised kernel memory, bypass KASLR, or trigger a + crash. + +6. Patch converts the manual scan to RtlStringCbLengthW, which takes + the remaining buffer size and fails if a terminator is not found + within that bound, removing the out-of-range dereference. + +Key structures / fields affected: + REG_MULTI_SZ / REG_SZ validation path (v10 == 0x2000) + psz pointer and cb length parameter. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +while ( *(_WORD *)SecurityDescriptor ) // OOB ❶ +{ + ... + SecurityDescriptor += 2 * (v28 >> 1); // advances to end +} +``` +```c +// after +while ( *(_WORD *)psz ) +{ + if (RtlStringCbLengthW((PCWSTR)psz, cbRemaining, &len) < 0) + return STATUS_INVALID_PARAMETER; + ... + psz = (PWSTR)((PBYTE)psz + len + 2); // only if safe +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode + SetupDiSetDeviceProperty / CM_Set_DevNode_Property + ↓ issues IOCTL to the device stack +Kernel + PiSetDevicePropertyData + → PnpValidatePropertyData (vulnerable) + → OOB read if buffer is missing final NUL terminator + +Attack Vector +-------------------------------------------------------------------- +A local, non-admin attacker that can call PnP property APIs or craft a +custom IRP_MJ_PNP request supplies a REG_MULTI_SZ buffer whose last +string is not terminated. When the kernel validates the buffer it +reads past the caller’s allocation and discloses adjacent kernel +memory. + +Patch Description +-------------------------------------------------------------------- +• Function signature changed to take a 64-bit aligned pointer. +• Manual UTF-16 scanning logic removed. +• Added RtlStringCbLengthW with explicit remaining-length argument – + provides built-in bounds checking. +• Additional size checks (<= 0xFFFE) and early returns added. +• Edge cases consolidated under the new helper Feature gate paths. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a specially crafted PnP property request could cause +ntoskrnl to read beyond a user-supplied buffer, leaking uninitialised +kernel memory to user mode or crashing the system. The issue is +classified as an information disclosure vulnerability (CVE-2025-55699) +with local attack scope. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code no longer performs unchecked pointer arithmetic; every +string length is now calculated through RtlStringCbLengthW, which fails +if the terminator is outside the remaining buffer. Therefore the out- +of-bounds read path is closed. No residual risk is evident unless +future changes bypass the helper or mis-calculate the remaining size. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55700_mprapi.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55700_mprapi.dll.txt new file mode 100644 index 0000000..26129a5 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55700_mprapi.dll.txt @@ -0,0 +1,117 @@ +{'date': 1763406222.9240863, 'patch_store_uid': '0f04ab1f-4275-427d-930e-a881574551fc', 'change_count': 37, 'confidence': 0.34, 'kb': 'KB5066835', 'cve': 'CVE-2025-55700', 'file': 'mprapi.dll'} +-------------------------------------------------------------------- +CVE-2025-55700 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Routing and Remote Access Service (RRAS) – mprapi.dll, routines +handling RTR_INFO_BLOCK / RAS connection & port thunking (e.g. +MprInfoBlockAdd / Set / Remove, MprThunk*32to64*, ConnectionEnum, etc.). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-125: Out-of-Bounds Read facilitated by 32-bit integer overflow / +insufficient size validation of user-supplied buffers. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. RRAS RPC entry points (e.g. MprAdminConnectionEnum, + MprAdminInterfaceDeviceGetInfo, ConnectionGetInfo) receive network + data that ultimately arrives as an RTR_INFO_BLOCK header or as a + 32-bit structure array. +2. Prior to the patch, helper routines such as + • MprInfoBlockAdd / MprInfoBlockSet / MprInfoBlockRemove + • MprThunkInterface_*32to64_* 0/1/2/3 + • MprThunkPort_*32to64_* 0/1/2 + accepted caller-supplied length fields (dwItemSize, dwItemCount, etc.) + without verifying that + (a) size*count fits into 32 bits + (b) the computed end offset is within the enclosing header + (lpHeader->Size) +3. The code performed arithmetic such as + newSize = itemCount * itemSize + headerSize + 23; + while the intermediate values were held in 32-bit registers. When + itemCount×itemSize overflowed UINT32, the subsequent memcpy copied + past the caller-allocated source buffer, causing an out-of-bounds + read of process memory that is later returned to the remote caller. +4. No structural sanity check on the existing RTR_INFO_BLOCK header was + performed. A malformed header with a small rbSize together with a + large internal offset (OffsetToFirstItem) therefore redirected the + memcpy source pointer outside the received blob. +5. The patch introduces the new helper ValidateRTRInfoBlockHeader and + injects it at every entry where an external header is consumed. The + patch also changes many arithmetic sequences to 64-bit, adds explicit + overflow checks (e.g. if (val > 0xFFFFFFFF) return ERROR_NOT_ENOUGH_…) + and early-outs on mis-matched sizes. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before – MprInfoBlockAdd (excerpt) +if ((unsigned __int64)(v6 * v7) > 0xFFFFFFFF) + return 534; // only after multiplication overflow!!! +memcpy_0(i, lpItemData, v9); +``` +```c +// After – first lines +if (!ValidateRTRInfoBlockHeader(lpHeader)) + return ERROR_INVALID_PARAMETER; +... +if ((unsigned __int64)(v6 * v8) > 0xFFFFFFFF) + return ERROR_NOT_ENOUGH_MEMORY; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends crafted RPC to RRAS endpoint (e.g. + \PIPE\ROUTER, opnum ConnectionEnum / GetInfo). +2. rpcss → mprapi!ConnectionEnum → internal + MprThunkConnection_WtoH → *32to64* helpers → + MprInfoBlockAdd / Set. +3. Malformed size fields cause integer overflow; memcpy reads past end + of attacker-controlled blob; leaked pile of heap memory returned in + RPC response. + +Attack Vector +-------------------------------------------------------------------- +Remote, unauthenticated SMB/RPC access to RRAS named-pipe interfaces; no +local privileges required. An attacker controlling network traffic to +an RRAS-enabled server can repeatedly request information with malformed +length values and harvest disclosed heap data. + +Patch Description +-------------------------------------------------------------------- +1. Introduces ValidateRTRInfoBlockHeader() and invokes it in every + *InfoBlock* routine. +2. Rewrites all size arithmetic to 64-bit, adds explicit checks against + 0xFFFFFFFF and against prior operand before every add / mul. +3. Rejects headers whose computed total length is < minimal structure + size, or where any internal offset falls outside header size. +4. Refactors thunk helpers to pass explicit buffer length (new + parameter), validates that caller-provided source length is + consistent and below protocol maximum before LocalAlloc/memcpy. +5. Propagates new prototypes throughout higher-level RPC stubs to pass + safe size values, eliminating previous casts that lost precision. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could read arbitrary bytes past the end +of the supplied buffer and receive the data in the RPC reply, leading to +information disclosure from the RRAS service process (SYSTEM). Although +no code execution is possible directly, leaked heap data can contain +pointers, credentials or ASLR seeds that significantly aid further +exploitation. + +Fix Effectiveness +-------------------------------------------------------------------- +• All arithmetic is now done in 64-bit and is guarded by overflow + checks; attempts to exceed UINT32 force ERROR_NOT_ENOUGH_MEMORY (534). +• ValidateRTRInfoBlockHeader guarantees internal offsets never extend + beyond total header length. +• Every thunk / enum routine now verifies caller-supplied lengths before + calling LocalAlloc / memcpy. +Given the comprehensive nature of the added checks, the original OOB +read condition can no longer be triggered with attacker-controlled data. + +-------------------------------------------------------------------- \ No newline at end of file diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55701_lsasrv.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55701_lsasrv.dll.txt new file mode 100644 index 0000000..fc9fe7e --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-55701_lsasrv.dll.txt @@ -0,0 +1,134 @@ +{'confidence': 0.14, 'patch_store_uid': 'b152918f-5cdf-4476-a46d-deb61ecd20b3', 'change_count': 30, 'file': 'lsasrv.dll', 'cve': 'CVE-2025-55701', 'kb': 'KB5066835', 'date': 1763407794.4692252} +-------------------------------------------------------------------- +CVE-2025-55701 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows LSASS service – lsasrv.dll (Lsar* / Lsap* RPC +interfaces and helper routines) + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / handle-spoofing leading to local +privilege-escalation (CWE-1287 – Improper Validation of Specified Type +of Input) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Many LSASS RPC entry points accepted a caller-supplied LSAPR_HANDLE +(pointer-sized value that should reference an internal LSAP_DB_HANDLE +object) and immediately performed privileged operations on the object +without first verifying that the supplied value actually represented a +valid, in-state LSASS database handle owned by the caller. + +Examples before the patch: +• LsarDeleteObject(void **Handle) simply executed + return LsapDeleteObject(Handle, 1); +• LsarClose(_QWORD *Handle) dereferenced the pointer chain and updated + reference counts without any sanity-check. +• LsarSetSecurityObject, LsarQuerySecurityObject and several helper + paths such as LsapBuildAndCreateToken and LsapDoPrivilegeUpdateOn + Account all trusted the incoming handle. + +Because LSASS runs as SYSTEM and the code executed inside the security +context of the process, a low-privileged but authenticated attacker +could craft or recycle an invalid / stale handle value and send it via +LSARPC. When LSASS dereferenced the fake pointer the code manipulated +arbitrary kernel-managed LSASS structures, enabling: +• deletion of arbitrary LSA objects (secrets, privileges, accounts), +• assignment of SYSTEM security descriptors to attacker-controlled + objects, +• modification of account privileges or system-access flags, +• reference-count corruption and use-after-free of LSASS objects. + +Ultimately these manipulations allow the attacker to obtain or inject a +SYSTEM token, resulting in a complete local privilege escalation. + +Affected structures / parameters +• LSAP_DB_HANDLE (referenced by first argument of Lsar* APIs) +• LSAPR_HANDLE values transmitted over MS-LSARPC (opnums involving + DeleteObject, Close, SetSecurityObject, QuerySecurityObject, etc.) +• Internal reference counts and SystemAccess bits changed via + LsarGet/SetSystemAccessAccount. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch: +```c +__int64 __fastcall LsarDeleteObject(void **a1) +{ + return LsapDeleteObject(a1, 1); // no validation +} + +__int64 __fastcall LsarClose(_QWORD *a1) +{ + ... + v3 = v2[22]; // blind dereference + ... // privileged work +} +``` +After patch: +```c +if (!wil::details::FeatureImpl<...>::__private_IsEnabled(...)) + return LsapDeleteObject(a1, 1); +result = LsapDbVerifyHandle(*a1, 0, 0, 0); // new sanity check +if ((int)result >= 0) + return LsapDeleteObject(a1, 1); +return result; // STATUS_INVALID_HANDLE +``` +Similar `LsapDbVerifyHandle()` calls were added to LsarClose, +LsarSetSecurityObject, LsarQuerySecurityObject and others. All legacy +call sites were switched from old helpers (LsapCloseHandle, +LsapDeleteObject) to new safe wrappers (LsarClose, LsarDeleteObject). + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens an LSARPC connection (\pipe\lsass) using any normal + account. +2. Attacker forges or re-uses a stale LSAPR_HANDLE value and invokes an + RPC method such as LsarDeleteObject, LsarSetSecurityObject or + LsarClose, passing the crafted handle. +3. Pre-patch LSASS dereferences the bogus pointer, manipulating + arbitrary internal objects (privileges, secrets, reference counts). +4. Crafted state yields SYSTEM privileges (e.g., enable SeDebug, write + arbitrary secret, or corrupt token structures). + +Attack Vector +-------------------------------------------------------------------- +Local authenticated user via the LSARPC interface (NT AUTHORITY +Authenticated Users) – no administrative rights required; only the +ability to issue LSA RPC calls. + +Patch Description +-------------------------------------------------------------------- +1. Introduced central validation routine `LsapDbVerifyHandle()` to + ensure a handle: + • points to readable memory, + • is of the correct object type, + • has not been closed or revoked. +2. Added new public wrappers (`LsarDeleteObject`, `LsarClose`, etc.) + that call the verifier before touching the object. +3. Re-wired all call sites to use the new wrappers and replaced direct + calls to `LsapCloseHandle` / `LsapDeleteObject`. +4. Inserted inline verification inside high-risk paths (e.g. + LsarSetSecurityObject, LsarQuerySecurityObject) when a feature flag + is enabled. +5. Updated global flag (`byte_180197744`, formerly 0x70C) and WIL + feature gates to allow controlled rollout. + +Security Impact +-------------------------------------------------------------------- +Before the fix a normal domain or local user could elevate to SYSTEM by +supplying a forged LSASS database handle and triggering privileged +operations, bypassing object access checks and corrupting LSASS memory. +Successful exploitation grants full control of the machine and its +secrets. + +Fix Effectiveness +-------------------------------------------------------------------- +All vulnerable entry points now fail early with STATUS_INVALID_HANDLE +if the supplied handle is not recognised, preventing misuse. The +validation is centralised and invoked on every sensitive path, closing +the privilege-escalation gap without functional regression for valid +clients. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58714_afd.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58714_afd.sys.txt new file mode 100644 index 0000000..42f04b1 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58714_afd.sys.txt @@ -0,0 +1,128 @@ +{'date': 1763402993.2758634, 'change_count': 51, 'confidence': 0.23, 'cve': 'CVE-2025-58714', 'file': 'afd.sys', 'patch_store_uid': 'd18657ac-5d16-40b0-bfc8-644001aeac77', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-58714 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) – functions +AfdSanInitEndpoint, AfdTdiCreateAO, WskProIRPGetNameInfo, and +WskProIRPGetAddressInfo. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Arbitrary Kernel Write (privilege +escalation). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The core defect sits in AfdSanInitEndpoint. Prior to the patch the +routine was declared as + AfdSanInitEndpoint( _QWORD a1 ) +but the compiled body unconditionally dereferenced the registers r9, +r10, rdx and r8 (labelled v1-v5 below) and used them as structure +pointers: + *(_QWORD *)(r10 + 96) = r9; // store caller-supplied ptr + *(_QWORD *)(r10 + 104) = rdx; // … + *(_QWORD *)(r10 + 112) = r8; // … +Because those registers were never validated, the caller that reaches +AfdSanInitEndpoint could fully control both + • the destination address (r10 + offset) and + • the value written (r9/rdx/r8). +This is a classical write-what-where condition inside the kernel and +allows an unprivileged process to overwrite arbitrary kernel memory +(eg. SYSTEM token pointers) and thus elevate its privileges. + +The patch changes the prototype to + AfdSanInitEndpoint( _QWORD Endpoint, + _QWORD SanContext, + _QWORD Provider ) +and re-implements pointer derivation as follows: + • v3 (the destination buffer) is fetched from *(SanContext+0x18), an + address that the driver itself previously set up. + • An _InterlockedIncrement64 on (Endpoint+0x40) ensures the endpoint + reference count is already >1; otherwise KeBugCheckEx is raised via + __fastfail(0xE). + • Subsequent member initialisations use only the verified v3 pointer + plus constant, in-object offsets. + +Therefore user-mode input can no longer select an arbitrary kernel +address, eliminating the write-what-where primitive. + +Side routines (AfdTdiCreateAO, WskProIRPGetNameInfo, WskProIRPGetAddressInfo) +were also hardened – replacing direct Afd*ReferenceClient calls with +RoReferenceEx, adding feature-flag based access checks and cleaning up +IRP handling – but these are defence-in-depth; the privilege-escalation +truly stemmed from the unchecked pointer use in AfdSanInitEndpoint. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (simplified) +AfdSanInitEndpoint(a1) +{ + // r9, r10, rdx, r8 are attacker-controlled + *(_QWORD *)(r10 + 96) = r9; + *(_QWORD *)(r10 + 120) = 0; + *(_QWORD *)(r10 + 104) = rdx; + *(_QWORD *)(r10 + 112) = r8; + ... // more writes into r10 region +} + +// fixed +AfdSanInitEndpoint(a1, a2, a3) +{ + v3 = *(QWORD*)(a2 + 0x18); // driver-owned buffer + if ( _InterlockedIncrement64((QWORD*)(a1 + 0x40)) <= 1 ) + __fastfail(0xE); + *(QWORD*)(v3 + 0x60) = a1; // safe writes inside object + *(QWORD*)(v3 + 0x68) = a2; + *(QWORD*)(v3 + 0x70) = a3; + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged process sends a crafted IOCTL/WSK request that ends in + afd!AfdSanInitEndpoint. +2. Because the kernel prototype required only one argument, the extra + registers (r9/r10/rdx/r8) still contain user-controlled values from + the earlier call frame. +3. The function writes those values into *(r10+offset) without any + validation. +4. Arbitrary kernel memory is overwritten, allowing privilege + escalation. + +Attack Vector +-------------------------------------------------------------------- +Local: any low-privilege user that can create or manipulate AF_UNIX +(AFDSAN) sockets or issue specific AFD IOCTLs can trigger the routine +and supply the malicious register values. + +Patch Description +-------------------------------------------------------------------- +• Corrected function prototype to include the missing parameters. +• Destination pointer now obtained from a trusted field inside the SAN + context (a2+0x18). +• Added _InterlockedIncrement64 + __fastfail to ensure valid endpoint + lifetime and avoid under-flowed reference counts. +• Replaced blind pointer stores with bounded writes to (v3 + constant). +• Ancillary functions now use new reference helpers and gated feature + checks, reducing future misuse. + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could perform an arbitrary 8-byte write +anywhere in kernel memory, leading to full SYSTEM compromise +(elevation of privilege). No user interaction is required beyond +running exploit code. + +Fix Effectiveness +-------------------------------------------------------------------- +Pointer destinations are now derived exclusively from internal +structures, and the additional reference-count + fast-fail logic +provides runtime enforcement. No further uncontrolled offset writes +are visible in the patched code path, so the specific arbitrary write +primitive is eliminated. A full audit of all callers is still +recommended, but the patch appears to close the identified hole. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58719_cdp.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58719_cdp.dll.txt new file mode 100644 index 0000000..bb3bee5 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58719_cdp.dll.txt @@ -0,0 +1,110 @@ +{'date': 1763403069.7412949, 'file': 'cdp.dll', 'kb': 'KB5066835', 'change_count': 643, 'confidence': 0.21, 'patch_store_uid': '76be1102-53fe-47f5-94be-28ece81636ed', 'cve': 'CVE-2025-58719'} +-------------------------------------------------------------------- +CVE-2025-58719 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (Cdpsvc) +cdp.dll – function chain around IWorkItemDispatcher::AddWorkItem and +Tip2 shared_data::on_result. + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free – CWE-416 + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine was the compiler-generated lambda helper +cdp::IWorkItemDispatcher::AddWorkItem__lambda_8c339cd5...(). + +1. The helper constructs a temporary std::function object on the + caller’s stack (array v5). +2. The concrete callable object is allocated on the heap via + std::_Global_new_std::_Func_impl_no_alloc...() and its pointer is + stored inside v5. +3. v5 is passed by reference to the dispatcher’s AddWorkItem virtual + method (v3). +4. As soon as the virtual call returns, the stack object v5 is + destroyed, which in turn frees the heap-allocated _Func_impl. +5. The dispatcher, however, may have kept a copy of the pointer to the + freed _Func_impl for deferred execution. Any later invocation of + the queued work item dereferences invalid memory, producing a classic + use-after-free inside the SYSTEM-privileged Cdpsvc process. + +Affected data structures / parameters + • std::function<bool(ULONG,const WCHAR*,const WCHAR*)> (v5) + • std::_Func_impl_no_alloc<lambda> (heap object) + • v3 – virtual table slot +0x18 (AddWorkItem) + +Because the freed memory contains attacker-controlled pointers, an +authenticated local user can achieve arbitrary code execution in the +service context, resulting in elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +v3 = *(__int64 (**)(__int64, char *))(*(_QWORD *)a1 + 24); +v6 = std::_Global_new_std::_Func_impl_no_alloc__lambda(...)(a2); +LOBYTE(v3) = v3(a1, v5); // dispatcher keeps copy of v5 +std::function<...>::~function(v5); // v5 freed immediately → UAF +``` +```c +// AFTER (simplified) +if (!tip2::details::g_test_interface_exception_guard) + return (*func)(*a1); +result = g_test_interface_exception_guard(*a1,0,0,0,a2); +if (!result) +{ + *(WORD *)(a2+42) = 0x401E; // set failure code + *(BYTE *)(a2+40) = 3; + return TestClose_0(...); // safe cleanup +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client code calls Cdpsvc API that eventually reaches + IWorkItemDispatcher::AddWorkItem. +2. The helper lambda builds a temporary std::function (v5) and passes it + to the dispatcher. +3. Dispatcher queues the callback for later execution. +4. Helper returns; std::function destructor runs and frees _Func_impl. +5. At a later time the dispatcher executes the queued work item and + dereferences the freed _Func_impl → UAF → potential RCE in SYSTEM. + +Attack Vector +-------------------------------------------------------------------- +A locally authenticated attacker sends crafted work-item requests to the +Connected Devices Platform Service, causing it to queue callbacks that +reference attacker-controlled std::function objects. When the service +later executes the queued work, it operates on freed memory, enabling +control of instruction flow and elevation to SYSTEM. + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the fragile lambda helper with +Tip2::details::shared_data<1,0,0>::on_result(). Key changes: + • Removed on-stack std::function construction and destruction. + • Introduced a persistent shared_data structure that survives until + callback completion, eliminating premature free. + • Wrapped the virtual call with g_test_interface_exception_guard to + detect failures and route to TestClose_0 for orderly teardown. + • Explicitly writes status code 0x401E and state 3 into the output + TipReportingInfo buffer when the guard detects an error. + +Security Impact +-------------------------------------------------------------------- +Before the patch, a normal user could trigger use-after-free in the +SYSTEM service process and run arbitrary code, yielding full privilege +escalation. The issue scores high for local EoP because exploitation is +reliable and the service runs as NT AUTHORITY\SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the lifetime mismatch by keeping the callable object +alive for the entire work-item lifecycle and by adding an exception +guard. No immediate path to reclaim freed memory remains, and the +additional guard prevents silent failures. Therefore the fix appears +complete for the identified UAF. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58719_cdpsvc.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58719_cdpsvc.dll.txt new file mode 100644 index 0000000..e582781 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58719_cdpsvc.dll.txt @@ -0,0 +1,112 @@ +{'change_count': 8, 'confidence': 0.34, 'date': 1763403082.9418578, 'file': 'cdpsvc.dll', 'cve': 'CVE-2025-58719', 'patch_store_uid': '0e54175f-9f22-44cd-8cdd-4e0fede69033', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-58719 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (cdpsvc.dll). +Faulty helper: lambda_dde3f900c43be7a54739f39c59f549c9_::operator(), +called from CDPComDevice::DeviceCallback::OnAppTargetListReceived(). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free triggered by a heap buffer under-allocation that +causes out-of-bounds writes and subsequent heap corruption. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. OnAppTargetListReceived() forwards its parameters to a std::function + wrapping lambda_dde3f900c43be7a54739f39c59f549c9_::operator(). + +2. The lambda receives: + a1 -> wrapper holding two fields + +0 : struct containing the source ICDPAppId + +8 : WORD *TargetCountPtr (number of char* targets) + +16 : QWORD *TargetStrings (array of char* pointers) + a2 -> output buffer (CDPComDeviceResult) where the lambda must + create an array of cloned strings and store its address at + offset +64. + +3. Original code allocated the pointer array with: + v6 = CoTaskMemAlloc( **(WORD**)(a1+8) ); + This size parameter is *count* rather than + count * sizeof(char*) (8 bytes on x64). For any count > 1 the + buffer is 7× too small, so the next line + *(_QWORD *)(a2+64)[i] = ... + writes past the end of the allocation. + +4. The overflow corrupts the heap’s control data or adjacent + allocations managed by CoTaskMemAlloc() (COM task allocator). The + corrupted pointers are later released by CoTaskMemFree() during + normal cleanup, freeing memory that is already overwritten and + still in use elsewhere – a classic use-after-free. + +5. Because cdpsvc runs as NT AUTHORITY\LOCAL SERVICE (or SYSTEM on some + SKUs) the corrupted heap region resides in a privileged process. + Attackers controlling the target list can therefore obtain + arbitrary code execution in that context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable allocation (before) +v6 = CoTaskMemAlloc(**(unsigned __int16 **)(a1 + 8)); // size == count +... +*(_QWORD *)(a2 + 64) = v6; // array of LPSTR +... +*(_QWORD *)(*(_QWORD *)(a2 + 64) + 8i64 * v4) = CoTaskMemAlloc(v8); +``` +```c +// fixed allocation (after) +SIZE_T sz = **(unsigned __int16 **)(a1 + 8); +if (FeatureEnabled) + sz *= 8i64; // sizeof(LPVOID) +*(_QWORD *)(a2 + 64) = CoTaskMemAlloc(sz); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Application (or attacker) + -> ICDPAppTargetList API + -> cdpsvc!CDPComDevice::DeviceCallback::OnAppTargetListReceived() + -> std::function thunk + -> vulnerable lambda operator() + • under-allocates pointer array + • copies each target string, writing past buffer + -> later cleanup frees corrupted memory => UAF / EoP. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user sends a crafted AppTargetList containing a +large number of target strings via the Connected Devices Platform IPC +channel (RPC/ALPC). By spraying heap blocks and controlling the count +field the attacker induces an out-of-bounds write that corrupts adjacent +allocator metadata, leading to use-after-free and elevation of +privileges. + +Patch Description +-------------------------------------------------------------------- +1. Computes allocation size as TargetCount * 8 bytes (pointer size). +2. Keeps the change behind a WIL feature flag, enabled in the patched + build. +3. Adds early bailout if any allocation fails (returns + E_OUTOFMEMORY). +4. Refactors std::function setup but no behavioral change relevant to + the bug. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, an attacker could achieve arbitrary heap overwrite +inside cdpsvc, resulting in use-after-free and potential code execution +with Local Service or SYSTEM privileges. This constitutes an Elevation +of Privilege vulnerability. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes the under-allocation bug by multiplying the element +count with sizeof(LPVOID). All write indexes remain unchanged, so no +further overflow is possible. Provided that the WIL feature flag is +shipped enabled by default, the fix fully mitigates the issue. No +regression or alternate path using the old size was observed in the +shown diff. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58722_dwmcore.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58722_dwmcore.dll.txt new file mode 100644 index 0000000..5e20c91 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58722_dwmcore.dll.txt @@ -0,0 +1,133 @@ +{'change_count': 96, 'cve': 'CVE-2025-58722', 'confidence': 0.27, 'kb': 'KB5066835', 'date': 1763403099.5620382, 'patch_store_uid': '9ea31a38-c5d1-473b-b782-3c02fef25a9c', 'file': 'dwmcore.dll'} +-------------------------------------------------------------------- +CVE-2025-58722 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Desktop Window Manager (dwmcore.dll) +Function: CBrushRenderingGraphBuilder::AddEffectBrush + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow (out-of-bounds write) +CWE-122 + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AddEffectBrush builds a rendering graph for an effect brush. At run +-time it first queries the compiled effect for the number of subgraphs +("subgraphCount"): + + subgraphCount = vCompiledEffect->GetSubgraphCount(); + if (subgraphCount != 1) + vector.resize(subgraphCount-1); + +The vector (type SubgraphOutput) is therefore allocated for indexes +0‥(subgraphCount-2) and its base pointer is kept in v16 (pre-patch) / +v14 (post-patch). + +Later, while iterating every input of every subgraph the code asks the +compiled effect for an *intermediate* producer index: + + idx = vCompiledEffect->GetIntermediateInputIndex(sg, input); + if (isIntermediate) + entry = v16 + 16 * idx; // 16-byte element size + ... write fields in entry ... + +No validation existed to ensure that the returned "idx" is smaller than +subgraphCount-1. A malicious or corrupted compiled-effect object could +return an arbitrarily large value, causing + + v16 + 16*idx ==> pointer past the end of the heap block + +and the subsequent writes corrupt heap metadata or neighbouring +objects. Because this code runs inside the DWM service (high +integrity) while processing data that originates from a user session, +heap corruption can be leveraged for privilege-escalation. + +Affected structures / variables: + • std::vector<SubgraphOutput> (base pointer v16 / v14) + • idx (unsigned int, untrusted) + • 16-byte SubgraphOutput entry written without bounds check + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +idx = vCompiledEffect->GetIntermediateInputIndex(sg, input, &isInter); +if (isInter) +{ + entry = v16 + 16i64 * idx; // no bounds check + if (*(DWORD*)entry != -1) + CRenderingTechniqueFragment::AddIntermediateInput(frag, *(DWORD*)entry); + else { + tmp = *(QWORD*)(entry+8); + *(QWORD*)(entry+8) = 0; // out-of-bounds write possible + ... + } +} + +// AFTER +idx = vCompiledEffect->GetIntermediateInputIndex(sg, input, &isInter); +if (isInter) +{ + if (FeatureEnabled && idx >= ((vector_end - v14) >> 4)) + FailFast(); // new bounds check + entry = v14 + 16 * idx; // safe + ... +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker supplies or controls a Composition effect brush that is + compiled into a CCompiledEffectTemplate. +2. DWM (high integrity) calls AddEffectBrush when building the visual + tree. +3. CompiledEffect::GetIntermediateInputIndex returns an oversized + index. +4. AddEffectBrush writes to vector[oversized] – heap buffer overflow. +5. Corrupted heap allows code execution in DWM, leading to elevation of + privilege. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. By crafting a specially-formed effect brush +(and triggering composition), the user feeds a malicious +CCompiledEffectTemplate to the DWM service, exploiting the unchecked +index. + + +Patch Description +-------------------------------------------------------------------- +The patch introduces a length check before using the returned index: + + if (idx >= vector.size()) FailFast(); + +The check is wrapped in a wil::Feature gate but ultimately guarantees +that any out-of-range value terminates the process before memory is +modified. All later writes therefore stay inside the allocated +SubgraphOutput vector. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a user-controlled compiled effect could corrupt the +heap in the DWM service, enabling local elevation of privilege up to +SYSTEM. The issue is classified as a heap-based buffer overflow +(CWE-122). + + +Fix Effectiveness +-------------------------------------------------------------------- +The added bounds check converts a silent out-of-bounds write into an +immediate FailFast, eliminating the overflow. No residual write paths +bypass the new condition, so the fix is considered effective, assuming +that the guarding feature flag is enabled on all supported Windows +builds. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58726_srv.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58726_srv.sys.txt new file mode 100644 index 0000000..bdb058e --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58726_srv.sys.txt @@ -0,0 +1,128 @@ +{'confidence': 0.16, 'date': 1763402995.0283902, 'kb': 'KB5066835', 'file': 'srv.sys', 'change_count': 1, 'patch_store_uid': '27610275-7ed4-47bf-b772-9560a968ff37', 'cve': 'CVE-2025-58726'} +-------------------------------------------------------------------- +CVE-2025-58726 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel-mode driver "srv.sys" (SMB server). Affected routine +is BlockingSessionSetupAndX(), responsible for processing legacy +SESSION_SETUP_ANDX requests and building the session object held in +the connection control block. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Authorization-bypass that results in +Elevation of Privilege (CWE-284). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. During processing of a SESSION_SETUP_ANDX request the server + eventually reaches a point where the caller has either been fully + authenticated or is a NULL/ANONYMOUS session (byte flag at + Session+0xCA == 0x202). + +2. For anonymous sessions the legacy implementation unconditionally + set + *(BYTE *)(Connection + 0x410) = 1 ; field @ offset 0x1040 + which later marks the connection as a **local loopback** session. + Internal policy code treats such sessions as coming from the + machine itself and grants them additional privileges (e.g. bypass + of SPN validation, access to administrative named pipes, etc.). + +3. No verification was performed to prove that the socket peer address + is really a loopback address (127.0.0.0/8 or ::1). A remote client + could therefore craft a valid SESSION_SETUP_ANDX, claim an + anonymous logon, and obtain the elevated capabilities reserved for + genuine local callers. + +4. The patch inserts an explicit call to the new helper + SrvValidateLoopbackAddress( CtxtHandle ) + right before the flag is set. The flag is now set **only** if + a) the H2E feature gate is disabled, or + b) the helper returns STATUS_SUCCESS, proving the peer really uses + a loopback address. + +5. If the validation fails, ExtraSmbBuffer receives the error code and + the execution path is redirected to the common error exit + (LABEL_143), causing the request to be rejected. + +6. Therefore the vulnerability was a missing address-origin check when + assigning privileged local status to a session. + +Parameters / structures involved + Connection +0x1040 (BYTE) IsLoopback / LocalMachine flag + Session +0x202 (BYTE) IsNullSession flag + Session +0x203 (BYTE) IsAdmin flag (later affected) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch (simplified): +```c +if (!*(BYTE *)(Session + 0x202)) // NOT authenticated +{ + *(BYTE *)(Connection + 0x1040) = 1; // Mark as local – NO CHECK! + goto AuthenticatedLabel; +} +``` +After patch: +```c +if (!Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_0() || + (ExtraSmbBuffer = SrvValidateLoopbackAddress( + (PCtxtHandle)(*(QWORD *)(Session+0xB0)+8)), + ExtraSmbBuffer >= 0)) +{ + if (!*(BYTE *)(Session + 0x202)) + { + *(BYTE *)(Connection + 0x1040) = 1; // set only after check + goto AuthenticatedLabel; + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client establishes SMB connection and sends crafted + SESSION_SETUP_ANDX packet. +2. BlockingSessionSetupAndX() parses the request and reaches the branch + where Session->IsNullSession is true. +3. Prior to the patch the code sets Connection->IsLoopback = 1 without + verifying the peer address. +4. Subsequent policy code identifies the session as local and grants + higher privileges, leading to privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Authenticated (or anonymous) attacker that can reach the SMB port +(445/TCP) submits a specially-crafted SESSION_SETUP_ANDX sequence from +any remote machine. Because the server never validated that the +endpoint is loopback, the session is treated as if it were initiated +locally, enabling privileged operations. + +Patch Description +-------------------------------------------------------------------- +• Introduces a call to SrvValidateLoopbackAddress() that checks the + socket’s peer address. +• Sets Connection+0x1040 only after the validation succeeds. +• Propagates STATUS_ errors when validation fails, redirecting control + flow to the common failure handler. +• Label renames and extensive WPP tracing updates are cosmetic; the + security fix is the new validation gate. + +Security Impact +-------------------------------------------------------------------- +Successful exploitation lets a remote network user obtain the +privileges reserved for local (loopback) sessions, effectively turning +an unauthenticated or low-privileged account into an elevated one on + the target machine. This is an Elevation of Privilege in the SMB +server context. + +Fix Effectiveness +-------------------------------------------------------------------- +The added explicit validation closes the direct bypass; the privileged +flag is no longer set for remote addresses. Assuming +SrvValidateLoopbackAddress() correctly recognises loopback addresses +and cannot be spoofed, the fix fully mitigates the discovered issue. +Regression risk is minimal because the change only affects the single +code path that grants the local privilege flag. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58727_cdpsvc.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58727_cdpsvc.dll.txt new file mode 100644 index 0000000..b4d1a6b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58727_cdpsvc.dll.txt @@ -0,0 +1,116 @@ +{'change_count': 8, 'kb': 'KB5066835', 'patch_store_uid': '0e54175f-9f22-44cd-8cdd-4e0fede69033', 'cve': 'CVE-2025-58727', 'date': 1763403034.082913, 'confidence': 0.25, 'file': 'cdpsvc.dll'} +-------------------------------------------------------------------- +CVE-2025-58727 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (cdpsvc.dll) – specifically +code that handles the "App Target List" reply inside +CDPComDevice::DeviceCallback::OnAppTargetListReceived(). + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow caused by incorrect size computation during +heap allocation (CWE-122). Although the vendor advisory labels it as a +race condition (CWE-362), the patched code shows the immediate root +problem is an undersized allocation that leads to memory corruption. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. OnAppTargetListReceived() builds a std::function that ultimately + invokes lambda_dde3f900c43be7a54739f39c59f549c9_::operator() (pre- + patch). + +2. The lambda receives: + a1 -> context structure containing + +0x08 : WORD *TargetCountPtr (number of strings) + +0x10 : QWORD *TargetArrayPtr (array of char* received + from the device) + a2 -> output buffer whose field + +0x40 : QWORD PointerArray (to be filled by lambda) + +3. Pre-patch allocation logic: + count = **(WORD **)(a1+8); // number of targets + buf = CoTaskMemAlloc(count); // ALLOCATES <count> BYTES + *(QWORD *)(a2+0x40) = buf; // stores the base pointer + + The code *assumes* that the buffer now holds an array of QWORDs and + proceeds to write one 8-byte pointer per target: + *(QWORD *)(buf + 8*index) = CoTaskMemAlloc(strLen); + +4. When count > 0 the first write already exceeds the allocated size + unless count >= 8. For typical inputs (count 1-4) this corrupts the + adjacent heap metadata (8-byte write vs 1-4 bytes allocated). + +5. Subsequent strcpy_s() calls copy user-supplied strings into the + newly allocated per-string buffers, providing attacker-controlled + data that can overwrite freed-list pointers or neighboring objects. + +6. cdpsvc runs as NT AUTHORITY\SYSTEM. A local attacker able to make a + crafted Connected-Device RPC call can therefore obtain arbitrary + read/write within the service process and elevate privileges. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Pre-patch (lambda operator) +size = **(unsigned __int16 **)(a1 + 8); // number of items +ptr = CoTaskMemAlloc(size); // alloc <count> bytes +*(QWORD *)(a2 + 64) = ptr; +... +*(QWORD *)(ptr + 8 * v4) = CoTaskMemAlloc(v8); // 8-byte write +``` +```c +// Post-patch +count = **(unsigned __int16 **)(a1 + 8); +size = count; +if (FeatureEnabled) + size *= 8; // allocate count*8 bytes +ptr = CoTaskMemAlloc(size); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client request (ICDP) -> cdpsvc!CDPComDevice::DeviceCallback:: +OnAppTargetListReceived() + -> constructs std::function wrapper + -> invokes lambda operator() + -> mis-sizes CoTaskMemAlloc() + -> heap overflow while storing per-string pointers + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated user can interact with the Connected Devices +Platform IPC interface and supply an App Target List containing an +arbitrary number of strings. By specifying more than one target the +service allocates an undersized buffer and overwrites heap metadata, +allowing crafted data to pivot execution to attacker-controlled code +inside the SYSTEM service. + +Patch Description +-------------------------------------------------------------------- +The fix introduces: +1. A size computation that multiplies the target count by sizeof(QWORD) + (8) before calling CoTaskMemAlloc(). This guarantees adequate space + for the pointer array. +2. A feature flag gate (wil::FeatureImpl) so the change can be + conditionally enabled. +3. Updated caller (OnAppTargetListReceived) to reference the new lambda + symbol and modern std::function wrapper, but functional change is + the corrected allocation size. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a non-privileged user could reliably corrupt the heap +in a SYSTEM process, leading to elevation of privilege or service +crash. Exploitation is local-only and requires the attacker to issue a +crafted AppTargetList message. + +Fix Effectiveness +-------------------------------------------------------------------- +Allocating count*8 bytes eliminates the immediate overflow. As long as +Feature_2578215227 is enabled on all supported systems the issue is +resolved. If the flag is disabled or reverted, the vulnerability could +re-appear. No additional synchronisation issues were addressed; none +were observed in the diff. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58728_microsoft.bluetooth.service.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58728_microsoft.bluetooth.service.dll.txt new file mode 100644 index 0000000..5dd033c --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58728_microsoft.bluetooth.service.dll.txt @@ -0,0 +1,142 @@ +{'patch_store_uid': '2050fd76-c48f-4b6a-8213-cb0e4e18e962', 'date': 1763403059.459626, 'change_count': 92, 'confidence': 0.19, 'kb': 'KB5066835', 'file': 'microsoft.bluetooth.service.dll', 'cve': 'CVE-2025-58728'} +-------------------------------------------------------------------- +CVE-2025-58728 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Bluetooth Service (microsoft.bluetooth.service.dll). The +affected routines belong to the WinRT implementation used by +BluetoothAdvertisementPublisher-related objects. + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Dangling Reference (CWE-416). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several WinRT getter helpers exposed internal COM objects without +proper reference management: + +1. GapAdvertisementPublisherStatusChangedArgsImpl:: + get_FixedDeviceAddress() +2. GapAdvertisementPublisherStatusChangedArgsImpl:: + get_SetRequestGUID() +3. RuntimeClassImpl<…>::GetWeakReference() + +Before the patch the two *get_* methods simply copied an internal +ComPtr (stored in the object at offsets 0xB0..0xC0 and 0xC8..0xD8) to +the caller-supplied pointer: + + *a2 = this[11]; // no AddRef + +The caller owns the returned IReference<> and will Release() it. +Because the service did **not** AddRef, the last Release() frees the +object that is still cached inside the service instance. Subsequent +service activity (for example inside +GapAdvertisementPublisherRequestStatus::Update or later status +notifications) re-uses that dangling pointer, yielding a classic +use-after-free. + +GetWeakReference() had a different but related bug. The vtable entry +was pointing to a completely unrelated helper +(wil::details::FeatureImpl::GetCachedFeatureEnabledState). That code +assumed a totally different object layout and performed arbitrary +bit-wise updates (_InterlockedAnd / _InterlockedCompareExchange) on the +first DWORDs of the Bluetooth runtime object. This corrupted the +cached weak-reference slot at offset +0x30 and could leave a freed +Microsoft::WRL::Details::WeakReferenceImpl object referenced from the +instance, again leading to UAF when another thread dereferenced the +weak reference. + +Key data involved + - this+0x30 / this+0x48 : cached IWeakReference pointer/flag field + - this+0xB0..0xD8 : ComPtr holding IReference<GapRemoteAddress> + and IReference<GUID> + - flag bytes at +0xB0+10, +0xC8+18 indicating presence of a value + +The absence of AddRef() and the vtable mismatch made the lifetime of +these internal COM objects unsynchronised with the external reference +count expected by WinRT, enabling controlled memory re-use inside the +bluetooth service process. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch get_SetRequestGUID +if (!a2) return E_POINTER; +*a2 = this[11]; // raw pointer leak, no AddRef +return S_OK; + +// pre-patch RuntimeClassImpl::GetWeakReference (actually wrong body) +*(_QWORD *)a2 = 0; +int state = *(_DWORD *)a1; // interprets bluetooth object as WIL data +// … manipulates the first 32 bits, potentially clearing ref flags … +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege code obtains IGapAdvertisementPublisher-related event + args from the bluetooth WinRT API. +2. Calls FixedDeviceAddress() or SetRequestGUID(). +3. Service copies out the internal pointer without AddRef and returns + it to the client. +4. Client releases the returned interface; refcount reaches zero and + the underlying IReference object is freed. +5. Later, while processing + GapAdvertisementPublisherRequestStatus::Update(), the service + accesses the cached pointer, now pointing to freed memory. +6. Carefully timed heap grooming lets the attacker place controlled + data at that address, achieving code execution in the bluetooth + service (running as LocalSystem), hence Elevation of Privilege. + + +Attack Vector +-------------------------------------------------------------------- +Local. Any sandboxed or ordinary user that can interact with the +Windows Runtime bluetooth APIs can trigger the faulty getter and free +the object at will. + + +Patch Description +-------------------------------------------------------------------- +The update introduces correct lifetime handling: + +1. Both *get_* methods now + • validate the presence flags, + • call WRL::Make<Nullable<…>> / MakeReferenceGUID() to create a **new + IReference** instance, + • AddRef() the created object before returning, and + • release internal temporary pointers correctly. +2. GetWeakReference() was entirely replaced. The new code + • creates a WeakReferenceImpl with CreateWeakReference(), + • writes it to the instance’s cache slot using an + InterlockedCompareExchange64 with a high-bit marker, + • handles the existing cached reference path with + SafeUnknownIncrementReference(), ensuring the weak reference stays + alive. +3. Additional argument/flag validation was added to + GapAdvertisementPublisherRequestStatus::Update() to avoid operating + on partially initialised structures. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a local attacker could reliably trigger a +use-after-free in the bluetooth service, re-allocate the freed memory +with attacker-controlled contents, and hijack control-flow inside a +LocalSystem process, resulting in privilege escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch converts raw pointer returns into properly reference-counted +objects and rewrites the weak-reference caching logic with atomic +safety. No further code paths returning internal ComPtrs without +AddRef were observed in the provided diffs. Provided the new object +creation helpers are correct, the specific UAF is eliminated. A full +audit of similar getters in other Gap* classes is still recommended. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58729_lsm.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58729_lsm.dll.txt new file mode 100644 index 0000000..86851b1 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58729_lsm.dll.txt @@ -0,0 +1,128 @@ +{'date': 1763402989.4541268, 'confidence': 0.45, 'cve': 'CVE-2025-58729', 'file': 'lsm.dll', 'change_count': 14, 'patch_store_uid': 'da4e902e-c230-48a6-bd3a-79fec5239c44', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-58729 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Session Manager (lsm.dll) – code paths handling the +ALPC tunnel between LSM and CSRSS. Affected classes: CCsrPipe, +CCsrMgr and helper routine SendLpcMessage / OnReplyMessage. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation resulting in a nullable-pointer de-reference +(CWE-1287, leads to denial-of-service / system crash). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The LSM service exchanges ALPC messages with CSRSS through CCsrPipe. +For reply traffic the structure _WINSTATIONREPLYMESSAGEMSG contains +three correlated fields: + • BYTE DoNotWait (offset +0x??) + • DWORD *pStatus (offset +0x20) + • DWORD *pResponse (offset +0x28) +When DoNotWait == 1, both pStatus and pResponse must be non-NULL so +that LSM can copy the kernel return value back to user space and +signal the event handle stored at +0x10. + +Prior to the patch the kernel side implementation assumed this +contract but never verified it: + 1. CCsrMgr::LpcWorker() fetched the CCsrPipe pointer from the ALPC + message attribute and dispatched sub-commands. + 2. For sub-command 2 (reply) it executed the following sequence + **((_DWORD**)v2 + 8) = *((_DWORD*)v2 + 14); + **((_DWORD**)v2 + 11) = *((_DWORD*)v2 + 20); + SetEvent(*((HANDLE*)v2 + 9)); + which blindly dereferences pResponse and pStatus. + 3. If a malicious client set DoNotWait == 1 but left the two + pointers NULL (or pointed them to invalid user memory) the kernel + would touch address 0x0 (or an arbitrary address) from kernel + mode, immediately triggering a bugcheck (KERNEL_MODE_EXCEPTION_NOT_HANDLED). + +A similar unchecked dereference existed in CCsrPipe::SendLpcMessage() +when LSM built such messages to send to CSRSS – an inconsistent +message could be emitted by the kernel and later received back, +propagating corrupted state. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Old code (no validation, lsm.dll): +```c +// CCsrMgr::LpcWorker – dispatching reply +**((_DWORD **)v2 + 8) = *((_DWORD *)v2 + 14); +**((_DWORD **)v2 + 11) = *((_DWORD *)v2 + 20); +SetEvent(*((HANDLE *)v2 + 9)); +``` + +New code (patched): +```c +// CCsrPipe::OnReplyMessage – verify first +if (*((_QWORD *)a2 + 1) && *((_QWORD *)a2 + 4)) { + **((_DWORD **)a2 + 1) = *(_DWORD *)a2; + **((_DWORD **)a2 + 4) = *((_DWORD *)a2 + 6); + SetEvent(*((HANDLE *)a2 + 2)); +} else { + _DbgPrintMessage(8, "OnReplyMessage: pResponse or pStatus is NULL"); + return STATUS_INVALID_PARAMETER; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker establishes an ALPC connection to LSM via CSRSS. +2. Attacker sends a fabricated _WINSTATIONREPLYMESSAGEMSG with: + DoNotWait = 1 + pStatus = NULL + pResponse = NULL +3. CCsrMgr::LpcWorker() routes message to CCsrPipe::OnReplyMessage() + (pre-patch this code did not exist, so dereference occurred inside + LpcWorker itself). +4. Kernel touches NULL -> bugcheck -> system reboot. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker in a normal user session that can communicate with +CSRSS/LSM over the documented WinStation ALPC port. No elevated +privilege is needed; only the ability to send a crafted reply message. + + +Patch Description +-------------------------------------------------------------------- +1. Added a dedicated function CCsrPipe::OnReplyMessage() that validates + pStatus and pResponse before dereferencing them and returns + STATUS_INVALID_PARAMETER on failure. +2. Updated CCsrMgr::LpcWorker() to call the new helper and to record + the returned NTSTATUS; it now emits debug output and avoids sending + a follow-up reply if OnReplyMessage failed. +3. Hardened CCsrPipe::SendLpcMessage(): + • Verifies input pointer ‘pMsg’ is non-NULL. + • For APINUMBER == 9 (reply) checks consistency between DoNotWait + flag and the two embedded pointers and logs unexpected + combinations. + • Rejects invalid combinations with STATUS_INVALID_PARAMETER. +4. All new checks are guarded by a Feature flag so they can be + selectively enabled; if disabled the old behaviour is preserved. + + +Security Impact +-------------------------------------------------------------------- +Before the fix an unprivileged user could cause a kernel NULL pointer +(or arbitrary pointer) dereference in lsm.sys, resulting in a system +crash and denial of service. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added NULL/consistency checks terminate processing early with +STATUS_INVALID_PARAMETER, preventing kernel dereference. The new code +paths cover both message reception (OnReplyMessage) and emission +(SendLpcMessage), closing the gap in both directions. No remaining +unchecked dereference of pStatus/pResponse was observed in the diff, +so the patch is deemed effective. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58730_combase.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58730_combase.dll.txt new file mode 100644 index 0000000..5e096a8 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58730_combase.dll.txt @@ -0,0 +1,128 @@ +{'kb': 'KB5066835', 'file': 'combase.dll', 'change_count': 13, 'confidence': 0.18, 'cve': 'CVE-2025-58730', 'patch_store_uid': 'd1e9d746-720d-4647-8cb2-ec67327d5cc6', 'date': 1763407735.6470878} +-------------------------------------------------------------------- +CVE-2025-58730 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft COM runtime (combase.dll) – class-information cache +(CClassCache) handling of CLSID entries. + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CClassCache keeps a global hash table ( _ClassEntries ) that maps one +or more GUIDs to a CClassEntry structure. New entries are allocated by +CClassEntry::CreateIncomplete(), then finalised by +CClassEntry::Complete(). While Complete() is running the global writer +lock may be released so other threads can populate the cache. + +1. Create() calls Complete(); if Complete() sets *fLockReleased = 1, + the writer lock was temporarily dropped. +2. After the lock is reacquired, Create() performs a + CCEHashTable::LookupCE() to see whether another thread has already + inserted an equivalent entry ( v14 is the newly allocated entry, + v13 the lookup result). +3. Prior to the patch: + if (v13) // duplicate found + { + CDropTargetAdapter *p = *pCE; // pCE still points at v14 + if (p) delete p; // free v14 + // *** pCE IS NOT UPDATED *** + } + The function then returns success, leaving the caller with *pCE + pointing to freed memory. Subsequent field access or virtual calls + on that pointer cause a use-after-free. +4. A very similar pattern existed in Complete(); when it had to create + a TreatAs entry it could free a temporary object, return, and leave + the outer entry holding an invalid list pointer. + +The affected members: + _dwFlags – bit 0x02 marks an "incomplete" entry. + _hashNode – embedded list node added to _ClassEntries. + _pTreatAsList – circular list of TreatAs relationships. + +Failure is purely logical – no bounds/overflow – therefore any caller +using the returned CClassEntry can corrupt the process heap and +redirect execution. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – CClassEntry::Create() +if ( !v13 ) { + // insert brand-new entry + ... + return Incomplete; +} +if ( v14 ) { + v19 = (CDropTargetAdapter *)*v9; // *pCE + goto LABEL_13; // delete v14 +} +... +// *pCE still holds freed v14 on return +``` +```c +// after patch +if (wil_feature && v14) { + if (*v10) delete *v10; // free old entry + if (Incomplete >= 0) + *v10 = (CDropTargetAdapter *)v14; // replace with live one + return Incomplete; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client thread A calls a COM API that causes CClassEntry::Create(). +2. Create() releases the writer lock during Complete(). +3. Thread B creates and inserts the same CLSID entry. +4. Thread A resumes, finds the duplicate, frees its own entry, returns + with *pCE still referencing the freed memory. +5. Any subsequent COM operation that touches that pointer triggers a + UAF. + +Attack Vector +-------------------------------------------------------------------- +Local or remote code that can create racing activation requests for the +same CLSID can force the UAF. In practice a malicious document or +script can spin up parallel COM activations inside the same process to +win the race and then spray heap data to hijack the dangling pointer. +No special privileges are required beyond the ability to load COM +objects. + +Patch Description +-------------------------------------------------------------------- +1. Create() + • Introduces logic to replace *pCE with the existing cache entry + after the temporary one is freed. + • Centralises hash-table insertion through CHashTable::Add(). + • Adds feature-flag gating and telemetry assertion. + +2. Complete() + • Factors Treat-As processing into CompleteTreatAs(). + • Adds explicit tests for the feature flag and _dwFlags to decide + whether another pass of Complete() is needed. + • Ensures _dwFlags is cleared only after the entry is stabilised. + +Together these changes guarantee the caller never receives a pointer to +freed memory. + +Security Impact +-------------------------------------------------------------------- +Exploiting the dangling CClassEntry pointer allows an attacker to +corrupt heap metadata or object vtables, leading to arbitrary code +execution in the context of the current process (preview pane / Outlook +/ Explorer etc.). Because many Microsoft applications use combase.dll, +this constitutes a high-risk local RCE. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code updates *pCE to point at the live duplicate entry +before returning, and identical safeguards were added to the TreatAs +path. No remaining paths free the entry without simultaneously +nullifying or replacing external references, therefore the original +UAF is eliminated. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58731_inetcomm.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58731_inetcomm.dll.txt new file mode 100644 index 0000000..16f2ab1 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58731_inetcomm.dll.txt @@ -0,0 +1,115 @@ +{'kb': 'KB5066835', 'patch_store_uid': '8699c3cd-7b74-419f-a1f1-7c37e23b0056', 'change_count': 5, 'date': 1763407649.3021667, 'file': 'inetcomm.dll', 'confidence': 0.2, 'cve': 'CVE-2025-58731'} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +inetcomm.dll (Windows Inbox COM Objects – Feature Management helper +routines located inside wil::details namespace) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free / Memory corruption caused by writing past the +end of a live object (improper pointer size handling and signature +mismatch). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +All affected helpers implement + wil::details::FeatureImpl<...>::GetCurrentFeatureEnabledState() + wil::details::FeatureImpl<...>::ReportUsage() + +The public prototype that the rest of the product calls is + GetCurrentFeatureEnabledState(int *pState); +so callers only provide a pointer to a 32-bit buffer. Inside the +pre-patch implementation the second parameter is treated as + _QWORD *a2 +and the very first operation is + *a2 = 0i64; // zero 8 bytes +which blindly stores eight bytes into the caller supplied 4-byte +buffer, corrupting the adjacent stack / heap memory belonging to the +caller. + +When the corrupted area contains a C++ object that is later destroyed, +its controlling fields (vtable, ref-count etc.) have already been +clobbered. The subsequent delete therefore frees a still referenced +object and a classic use-after-free window appears. Attackers that can +invoke the COM objects (eg. a sandboxed script or a crafted e-mail +message processed by the Inbox) can reliably control the overwritten +area and finally redirect execution. + +A second contributing bug is the mismatch between the on-disk +ReportUsage() prototype (expected bool as 2nd parameter) and the way it +was called from GetCurrentFeatureEnabledState. The extra stack +argument was interpreted as a boolean flag living inside the now +corrupted memory region, extending the corruption into a controlled +use. + +Structures / fields affected + • 32-bit FEATURE_ENABLED_STATE output buffer provided by caller + • Adjacent 4-byte slot – typically the object instance pointer or a + vtable pointer on the stack. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old – TestLabVal::GetCurrentFeatureEnabledState +* a2 = 0i64; // 8-byte write into 4-byte buffer +*(_DWORD *)a2 = v7; // later partially rewritten +``` +```c +// old – ReportUsage signature mismatch +__int64 ReportUsage(_DWORD *a1, unsigned __int8 a2, ...); +// called with 64-bit 2nd argument → stack mis-alignment +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client code allocates a 4-byte variable on stack and calls + FeatureImpl::GetCurrentFeatureEnabledState(&state); +2. The function executes '*a2 = 0i64;', overwriting the next 4 bytes on + the caller’s stack frame. +3. Normal execution continues; later the corrupted object is destroyed + (Release / delete) while still referenced elsewhere → freed but + alive. +4. Subsequent dereference of the dangling pointer lets the attacker + execute controlled data as code. + +Attack Vector +-------------------------------------------------------------------- +Any process that can obtain an interface pointer to the affected Inbox +COM objects and call GetCurrentFeatureEnabledState – for example code +executed by the mail reader while processing a crafted message – can +supply attacker-controlled stack/heap layouts and gain code execution. +No special privileges are required. + +Patch Description +-------------------------------------------------------------------- +The patch makes the following corrective changes: +1. Changes local variables so the 2nd parameter is treated as a 32-bit + buffer (v7/v8) and removes the 8-byte store; only 4-byte accesses + are now performed ( *(_DWORD *)a2 = ... ). +2. Always ORs in the valid-bit ( ... | 1 ) instead of writing the full + 64-bit slot. +3. Fixes ReportUsage prototype – the 2nd parameter is now an __int64 + (ReportingKind); call-sites were updated accordingly, eliminating the + stack-layout mismatch. + +Security Impact +-------------------------------------------------------------------- +Pre-patch builds let untrusted input corrupt adjacent memory, leading to +use-after-free and ultimately arbitrary code execution inside the +calling process. Because the functions reside in a COM component that +is available to low-privilege code, the flaw provides a local privilege +escalation / sandbox escape and, in the e-mail scenario, remote code +execution in the Outlook (or any InetComm-based) process. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated code confines all writes to the intended 4-byte buffer and +realigns function signatures, removing the memory clobbering that led +to dangling pointers. No additional unsafe writes are observable in +the patched diff, so the immediate overwrite/UAF vector is closed. A +full regression of every FeatureImpl instantiation is still advisable +but the specific root cause is addressed. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58732_combase.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58732_combase.dll.txt new file mode 100644 index 0000000..c0b9230 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58732_combase.dll.txt @@ -0,0 +1,118 @@ +{'change_count': 13, 'patch_store_uid': 'd1e9d746-720d-4647-8cb2-ec67327d5cc6', 'confidence': 0.21, 'cve': 'CVE-2025-58732', 'file': 'combase.dll', 'date': 1763407725.8968651, 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-58732 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows COM runtime (combase.dll) – class-cache logic in +CClassCache::CClassEntry::{Create,Complete} that manages the global +hash table of CLSID metadata. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (dangling pointer dereferenced after the +instance has been destroyed). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Every COM CLSID that is activated is tracked by a CClassEntry + object and stored in the global hash _ClassEntries. +2. CClassCache::CClassEntry::Create() first builds a temporary + CClassEntry via CreateIncomplete(), then finalises it by calling + Complete(). +3. Complete() may discover that another thread has already + materialised an identical entry. In that situation it sets + *pfLockReleased, releases the cache writer-lock, and returns the + pointer of the pre-existing entry (v13 in the old listing). +4. The caller (Create()) frees its own temporary object when the + duplicate (v13) is found, but then continues to use a local copy + of the *original* pointer (v14) to: + • read _dwFlags, + • build an SMultiGUIDKey structure, + • possibly insert the entry into the hash table. + Because the destructor has already executed, v14 now points to + freed memory – a classic UAF. +5. An attacker who can force the duplicate-found path (by racing two + activations of the same CLSID) gains the ability to cause the + class-cache code to operate on reclaimed memory. Carefully + shaping heap contents allows corruption of control data and + eventually arbitrary code execution in the COM broker process. +6. A similar dereference of a freed entry existed in the Treat-As + handling path of CClassEntry::Complete(). Recursive calls could + free the current object while outer frames continued to use it. + +Key internal fields / parameters involved + _dwFlags – flag bit 0x10 (SxS hash) and 0x20 (overflow) + _hashNode – embedded list links written after free + pfLockReleased – signals that the writer lock was dropped + pCE / v14 – dangling pointer to the freed CClassEntry + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch – CClassEntry::Create() +if ( fLockReleased ) { + PartitionIDForClassInfo = GetPartitionIDForClassInfo(pCI); + v13 = CCEHashTable::LookupCE(v21, dwClsHash, rclsid, PartitionID); +} +v14 = *v9; // *v9 was already deleted when v13!=0 +if ( !v13 ) { + dwFlags = v14->_dwFlags; // UAF: object already freed + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client (or attacker) activates the same CLSID concurrently on two + threads / processes. +2. Thread-A: enters Create(), builds entry-A, drops writer lock in + Complete(), then continues. +3. Thread-B: finishes first, inserts entry-B into hash table. +4. Thread-A: LookupCE() returns non-NULL -> frees entry-A, but still + dereferences v14 (entry-A) afterwards. +5. Freed memory gets overwritten by attacker-controlled allocations + -> arbitrary write/execute when cache later processes corrupted + structures. + +Attack Vector +-------------------------------------------------------------------- +Local, non-admin attacker executing code that repeatedly and +concurrently CoCreateInstance()’s a vulnerable in-box COM class. +No additional privileges are required; exploitation occurs inside the +combase broker which runs with high integrity. + +Patch Description +-------------------------------------------------------------------- +1. Early return if pCI is NULL – eliminates a degenerate path. +2. Re-organised logic so that the temporary CClassEntry is *never* + touched after it might have been deleted: + • destructor is called first + • subsequent operations use the duplicate pointer (v14) or build + a new hash entry only when duplicate == NULL. +3. Added wil feature gating and telemetry asserts to catch unexpected + lock-drop scenarios. +4. Introduced helper CompleteTreatAs() which finalises Treat-As + chains without re-using possibly freed objects. +5. In Complete(), equality check replaced unsafe pointer arithmetic + comparison; flow simplified so _dwFlags is cleared only after the + object is confirmed alive. + +Security Impact +-------------------------------------------------------------------- +The bug lets unprivileged code trigger a deterministic use-after-free +in the COM runtime. Because freed memory is immediately reused for +hash-node links, the attacker can gain control of linked-list pointers +and ultimately execute arbitrary code in the context of the COM broker +(process hosting combase.dll), leading to a local elevation of +privilege or code execution with elevated rights. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched functions no longer dereference the temporary object after +it might have been freed; the destructor is invoked only after all +needed fields have been copied, and alternative paths use the live +entry. Additional telemetry and feature gates make regressions easy +to detect. No remaining path that writes to _hashNode or _dwFlags can +execute on a dangling pointer, so the UAF is effectively removed. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58733_combase.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58733_combase.dll.txt new file mode 100644 index 0000000..27d8a3a --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58733_combase.dll.txt @@ -0,0 +1,146 @@ +{'file': 'combase.dll', 'confidence': 0.33, 'patch_store_uid': 'd1e9d746-720d-4647-8cb2-ec67327d5cc6', 'change_count': 13, 'kb': 'KB5066835', 'cve': 'CVE-2025-58733', 'date': 1763406148.6111574} +-------------------------------------------------------------------- +CVE-2025-58733 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft COM base (combase.dll) – class–activation cache +(CClassCache) logic, specifically the functions +CClassCache::CClassEntry::Create and +CClassCache::CClassEntry::Complete. + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / stale pointer following a lock release +(CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. During COM activation the runtime creates a new cache entry + by calling + CClassEntry::Create(rclsid, dwClsHash, pCI, … , &pCE) + while holding the global writer lock _mxs. + +2. In order to obtain Treat-As information, Create() calls + CClassEntry::Complete(pCE, treatAsClsid, &fLockReleased) + which may temporarily release the writer lock. When the lock + is dropped another thread can insert a fully-initialised entry + for the same CLSID into the global hash table and destroy the + incomplete one that *this* thread is still holding through the + **pCE** output parameter. + +3. On return, the old implementation trusted the pointer stored in + *pCE (variables v11 / v14) even when the function signalled that + the lock had been released (fLockReleased == 1). It immediately + dereferenced fields such as + v14->_dwFlags + v14->_hashNode + v14->_guids + to decide whether to link the entry into + CClassCache::_ClassEntries or to free it. If another thread had + already freed the object, these reads operated on reclaimed + memory, resulting in a classic use-after-free. + +4. A similar pattern existed in CClassEntry::Complete where, after + releasing and re-acquiring the writer lock, the code re-used the + stale *this pointer when building the Treat-As list, again + without re-validating that the entry was still live. + +5. The corrupted cache entry resides in process-wide global memory + and is later reachable through normal COM activation paths, + allowing an attacker to obtain control over a freed object that + is subsequently reused by the system. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – stale pointer dereferenced after lock drop +if (fLockReleased) { + v13 = CCEHashTable::LookupCE(v21, dwClsHash, rclsid, partId); +} +v14 = *v9; // v14 may already be freed +if (!v13) { + dwFlags = v14->_dwFlags; // UAF read + key.aGUID = v14->_guids; // UAF read + ... // further use +} +``` + +```c +// after patch – re-lookup or bail out before dereference +if (fLockReleased) { + v14 = LookupCE(...); +} +if (FeatureEnabled && ((*pCE)->_dwFlags & 0x40)) { + if (!v14) { // duplicate disappeared – unexpected + MicrosoftTelemetryAssertTriggeredNoArgs(); + goto safe_exit; + } +} else if (!v14) { + // safe path that still owns *pCE + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client thread A + CoCreateInstance(EXE/COM object) + -> activates CLSID X + -> CClassEntry::Create (allocates entry + and later releases lock in Complete) + +Concurrent thread B + Performs the same activation while A’s + lock is released, inserts its own entry + for CLSID X and frees the incomplete one + that A still references. + +Thread A resumes and dereferences freed +memory, leading to UAF. + +Attack Vector +-------------------------------------------------------------------- +A local, low-privileged attacker spawns two or more concurrent COM +activations for the same CLSID that is not yet present in the class +cache. Carefully timing the requests causes the race window during +which the target thread dereferences the freed entry. Because the +object memory is under the global heap, an attacker can groom the +heap to obtain control of the freed slot, achieving arbitrary code +execution in the caller’s context (often SYSTEM for service hosts). + +Patch Description +-------------------------------------------------------------------- +1. Early NULL-parameter guard was added. +2. After relocking, the code now: + • Re-looks up the CLSID and uses the new pointer, or + • Verifies a feature flag (bit 0x40 in _dwFlags) and aborts with + telemetry if the pointer disappeared. +3. The code that inserts the entry into the global hash table + (CHashTable::Add) now uses a freshly validated pointer that is + known to be still owned by the current thread. +4. Similar safety logic and a helper + CClassEntry::CompleteTreatAs + were added to Complete(), preventing reuse of an invalid *this + when building Treat-As lists. +5. Multiple calls to MicrosoftTelemetryAssertTriggeredNoArgs() were + inserted to record unexpected states. + +Security Impact +-------------------------------------------------------------------- +Exploiting the race gives an attacker a dangling COM cache entry +that they can overlap with controlled data. Subsequent activation +can lead to arbitrary code execution inside any process that uses +COM (including elevated service processes), turning the issue into +local privilege escalation or, via DCOM, remote code execution. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch removes all dereferences of cache entries that are not +re-validated after a lock drop, thereby closing the fundamental +use-after-free window. Additional telemetry ensures unexpected +states are detected in production. Unless another, independent +path manipulates _pTreatAsList without holding the lock, the fix +appears complete; no residual UAF scenario was observed in the +patched logic. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58734_combase.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58734_combase.dll.txt new file mode 100644 index 0000000..e541483 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58734_combase.dll.txt @@ -0,0 +1,108 @@ +{'cve': 'CVE-2025-58734', 'change_count': 13, 'date': 1763407789.965682, 'file': 'combase.dll', 'patch_store_uid': 'd1e9d746-720d-4647-8cb2-ec67327d5cc6', 'confidence': 0.34, 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-58734 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows COM run-time (combase.dll) – channel / marshaling +helpers, primarily NdrExtpProxyInitialize and related RPC proxy +helpers that manage IRpcChannelBuffer reference counting. + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NdrExtpProxyInitialize is executed by every generated COM proxy stub +before the first remote call. Its job is to copy the caller supplied +IRpcChannelBuffer* (held in pThis[3]) into the per-call _MIDL_STUB_ +MESSAGE structure and take a reference so that the channel object +remains alive for the duration of the call. The relevant excerpt is +reproduced in the next section. + +The routine recovers the AddRef function pointer from the channel +vtable (v8->AddRef). It then performs a dubious equality test: + + if (AddRef == &CClientChannel::AddRef) + CClientChannel::AddRef(v8); + else + AddRef(); // <-- no parameter passed + +On x64 the this-pointer must be delivered in RCX. The indirect call +made through AddRef() in the else branch passes *no* argument, so RCX +contains whatever value happened to be in the register at that moment +(routinely a stale stack pointer). Any channel object whose AddRef +implementation is *not* exactly CClientChannel::AddRef therefore +executes with an invalid this-pointer. Results are undefined but most +commonly: + +1. The real object never has its reference count incremented. +2. When the proxy later issues NdrExtpProxySendReceive, the channel + object may already have been released and freed. +3. The freed memory is then used as an IRpcChannelBuffer, yielding a + classic use-after-free that an attacker can exploit by re-allocating + controlled data in the same region. + +Because the bug is hit during proxy initialisation it is reachable +from *any* marshalled interface pointer and requires no special +privileges – the attacker only needs to arrange for a crafted channel +class (or any non-CClientChannel implementation) to be supplied to the +proxy. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// combase!NdrExtpProxyInitialize (before patch) +... +v8 = (IRpcChannelBuffer *)pThis[3]; +... +AddRef = (void (*)(void))v8->AddRef; +if ((char *)AddRef == (char *)CClientChannel::AddRef) + CClientChannel::AddRef(v8); // correct: passes v8 in RCX +else + AddRef(); // WRONG: RCX not initialised +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client receives a marshalled interface pointer. +2. CoUnmarshalInterface -> proxy stub -> NdrExtpProxyInitialize. +3. Proxy holds reference to supplied IRpcChannelBuffer. +4. AddRef executed without this-pointer – no refcount increment. +5. Reference released elsewhere, channel buffer freed. +6. First method call on proxy -> NdrExtpProxySendReceive uses freed + buffer, leading to memory corruption and potential code execution. + +Attack Vector +-------------------------------------------------------------------- +Local or remote attacker supplies a crafted COM object or marshalled +stream that results in a proxy holding a non-CClientChannel channel +implementation. Typical delivery vectors: + +• Malicious document/installer that embeds a custom channel object. +• Any medium integrity process interacting with an elevated COM + service (lateral privilege escalation). + +Patch Description +-------------------------------------------------------------------- +Not available in the supplied diff. (The after-patch listings were +omitted, so the exact code change is unknown.) + +Security Impact +-------------------------------------------------------------------- +A successful attacker gains the ability to execute arbitrary code in +the context of the process that unmarshals the interface pointer. +When that process runs with elevated privileges (e.g., SYSTEM service +or admin UI), full privilege escalation is possible. The condition is +triggered before any application-level checks, so authentication or +package identity does not mitigate it. + +Fix Effectiveness +-------------------------------------------------------------------- +No post-patch code was provided; therefore correcteness of the fix +cannot be verified. A complete mitigation would require ensuring that +AddRef is always invoked with a valid this-pointer (for example +`v8->AddRef(v8);` or `IUnknown::AddRef(v8)`) regardless of the concrete +channel implementation, and auditing all similar call sites. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58736_combase.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58736_combase.dll.txt new file mode 100644 index 0000000..65ce755 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58736_combase.dll.txt @@ -0,0 +1,114 @@ +{'patch_store_uid': 'd1e9d746-720d-4647-8cb2-ec67327d5cc6', 'date': 1763407723.2457254, 'confidence': 0.24, 'kb': 'KB5066835', 'file': 'combase.dll', 'change_count': 13, 'cve': 'CVE-2025-58736'} +-------------------------------------------------------------------- +CVE-2025-58736 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +combase.dll – runtime COM infrastructure, class-activation cache +(CClassCache and its nested CClassEntry objects). + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Dangling Pointer (CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CClassCache keeps a global hash table that maps CLSIDs to +CClassEntry objects. CClassEntry::Create() performs a two-phase +construction: + 1. CreateIncomplete() allocates a new entry and returns it through + the caller-supplied **pCE. + 2. Complete() finalises the entry and may temporarily release the + cache writer lock ( *pfLockReleased = 1 ). + +If Complete() had to release the lock another thread can insert an +entry for the same CLSID before the first thread reacquires the lock. +After Complete() returns with fLockReleased==1, Create() performs a +LookupCE() to see whether an equivalent entry has appeared. When one +is found ( variable v13 / v14 in the old build ) the old code destroys +its own newly allocated object: + CDropTargetAdapter::`scalar deleting destructor'(*pCE, ...); + +However the function forgets to update the caller-visible pointer +*pCE so the caller continues to hold a dangling pointer that now +references freed memory. Subsequent cache operations, reference +counting or interface method calls operate on reclaimed memory and +can be steered by an attacker once the freed region is reused – a +classic Use-After-Free condition. + +An analogous pattern existed in CClassEntry::Complete() for TreatAs +handling: when a TreatAs target already existed a dangling pointer +could again be returned to the caller. + +Affected fields / parameters + * pCE – out parameter that receives the final CClassEntry pointer. + * _dwFlags bit 0x02 (CCE_INCOMPLETE) used to detect Complete() state. + * Global hash table CClassCache::_ClassEntries. + * Writer lock CClassCache::_mxs. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before – object freed but caller still holds old pointer +if (v14) { // duplicate found + v19 = (CDropTargetAdapter *)*v9; // old entry + if (*v9) + CDropTargetAdapter::`scalar deleting destructor'(v19, v12); + /* *v9 is not reassigned – dangling pointer returned */ +} + +// After – pointer replaced with the canonical entry +if (*v10) + CDropTargetAdapter::`scalar deleting destructor'(*v10, v13); +if (Incomplete >= 0) + *v10 = (CDropTargetAdapter *)v14; // <- fix +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client calls CoCreateInstance / activation for CLSID X from two + threads concurrently. +2. Thread A enters CClassEntry::Create() – allocates Entry A. +3. Complete() releases writer lock to query TreatAs; fLockReleased=1. +4. Thread B meanwhile inserts Entry B for the same CLSID into the + cache and returns. +5. Thread A resumes, detects duplicate, frees Entry A but still + returns pointer to Entry A -> dangling pointer. +6. Caller later dereferences pointer, leading to UAF. + +Attack Vector +-------------------------------------------------------------------- +Local (or remote via COM/RPC) code issues concurrent activation calls +for the same CLSID, wins the race to provoke duplicate insertion, and +then triggers subsequent method calls on the freed object. Memory +re-use under attacker control enables arbitrary code execution. + +Patch Description +-------------------------------------------------------------------- +1. After detecting an existing entry the function now: + • frees the temporary object; and + • **assigns *pCE to the canonical entry** before returning. +2. Similar logic added to Complete() via new helper + CClassEntry::CompleteTreatAs(). +3. Added Feature-flag checks and telemetry asserts but they are + cosmetic; the critical fix is the pointer reassignment. +4. Refactored bucket insertion through CHashTable::Add() – no logic + change. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any caller of CClassEntry::Create() / Complete() +could receive a pointer to freed memory, leading to: + • Arbitrary code execution in the COM runtime process context. + • Possible privilege escalation if activated under elevated COM + server security settings. +The issue is rated Remote Code Execution because COM activation can +be driven cross-process via RPC. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched build guarantees that the out parameter always references +valid live memory before returning. All observed UAF paths have been +covered in both Create() and Complete(); no residual dangling pointer +paths are visible in the diff. The fix appears effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58736_oleaut32.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58736_oleaut32.dll.txt new file mode 100644 index 0000000..9655d73 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58736_oleaut32.dll.txt @@ -0,0 +1,136 @@ +{'file': 'oleaut32.dll', 'confidence': 0.11, 'change_count': 3, 'date': 1763407745.4093723, 'cve': 'CVE-2025-58736', 'patch_store_uid': 'e710d0bf-5d51-4d50-b751-a1ccb9cf2982', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-58736 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +oleaut32.dll – Windows Implementation Library (WIL) feature tracking +helpers +Functions affected: + • wil::details::FeatureImpl<…TestLabVal>::GetCurrentFeatureEnabledState + • wil::details::FeatureImpl<…TestGateImp>::GetCurrentFeatureEnabledState + • wil::details::FeatureImpl<…TestLabVal>::ReportUsage + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416 Use After Free / stale pointer dereference caused by parameter +mis-interpretation and improper life-time checks. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The three helper routines shown in the diff are part of the WIL feature +instrumentation layer that ships inside oleaut32.dll. They convert the +low-level FEATURE_ENABLED_STATE flags returned by +WilApi_GetFeatureEnabledState into a 32-bit bit-field that is stored in +an application-supplied buffer (*a2) and – in some cases – they forward +usage information to the telemetry service via ReportUsageToService. + +Prior to the patch the code suffered from two closely related defects: + +1. Parameter–type mismatch in the ReportUsage helper + ------------------------------------------------- + The real C++ signature of ReportUsage is + ReportUsage(bool isEnabled, wil::ReportingKind kind, + unsigned __int64 extraTag) + + but the compiled routine interpreted the second logical parameter as + an 8-bit value (unsigned __int8 a2) and treated the 64-bit + third parameter as a normal 64-bit integer without validating the + call site. Because every call site inside Get*EnabledState passed a + full 32-bit ReportingKind enum, the callee saw the upper three bytes + of that enum as an *extra* stack argument. This shifted the whole + register/stack argument layout by one slot so that the pointer + v11/v4 that is later stored in R8 and forwarded to + ReportUsageToService was in fact attacker-controlled stack data + residing beyond the current frame. + + As soon as ReportUsageToService asynchronously dereferenced that + pointer the process touched already freed stack memory – a classic + use-after-free.​ The attacker only had to be able to invoke any + WinRT/COM component that eventually calls these WIL helpers (for + instance through scripting or Office macros). + +2. Missing lifetime/ownership validation in GetCurrentFeatureEnabledState + --------------------------------------------------------------------- + The two GetCurrentFeatureEnabledState variants populated the caller + supplied buffer *a2 with a mixture of fixed and computed bits. When + the FEATURE_ENABLED_STATE mask contained the value 0, internal + variables v6, v8 and v11 were *never initialised* before they were + OR-combined into the final word that is written back to the caller. + + A carefully fuzzed FEATURE_ENABLED_STATE value therefore caused + stale stack contents (including the aforementioned invalid pointer) + to be copied into *a2, again leading to a dangling pointer that was + later consumed by ReportUsage. + +Patch analysis shows that Microsoft fixed the defect by + • declaring the second parameter as a 64-bit integer (RDX) so that the + original register layout is preserved, + • replacing the caller supplied ‘kind’ with a constant 1 (safe value), + • zero-initialising all working variables (v6 = 0, v7 = 64) before they + participate in any expression, and + • forcing the least-significant bit in the result word (*a2 |= 1) + thereby making sure the state field can never encode a pointer. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before – parameter mismatch, stale v11 copied +v11 = a4; // a4 is really the upper 32 bits of ReportingKind +... +return ReportUsageToService(a1 + 2, 54237977i64, + ((unsigned int)v4 >> 10) & 1, + ((unsigned int)v4 >> 11) & 1, + &v9, v6, 0); // v4 may point to freed stack memory +``` +```c +// after – correct typing and safe constant +v9 = 3; // structure tag +v8 = 0; // clear pointer +return ReportUsageToService(a1 + 2, 54237977i64, + (v4 >> 10) & 1, (v4 >> 11) & 1, + &v8, 1, 0); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls a COM/WinRT API that ends up in + GetCurrentFeatureEnabledState(). +2. Function builds state bits and calls ReportUsage() with the malicious + ReportingKind value. +3. Inside ReportUsage() parameter mis-alignment shifts the stack; + a4 (really stack garbage) is copied into v11/v4. +4. ReportUsageToService asynchronously dereferences v4 -> UAF / RCE. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged code that is able to create any Inbox COM object +which internally relies on the affected WIL feature helpers. No +additional privileges are required. + +Patch Description +-------------------------------------------------------------------- + • Corrected ReportUsage() prototype: second argument widened from + unsigned __int8 to __int64; first argument typed as unsigned int*. + • Zero-initialised temporary variables (v6, v7) in the two + GetCurrentFeatureEnabledState() functions. + • Simplified flag handling and always sets bit 0 in the result word to + prevent pointer masquerading. + • Removed paths that attempted to inspect unrelated features, thereby + eliminating uninitialised-variable accesses. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could gain code-execution in the context of +any process that called these helpers. Because oleaut32.dll is loaded +into almost every Windows process the bug constitutes a reliable local +EoP / RCE vector. The official disclosure lists it as +CVE-2025-58736 – Inbox COM Objects (Global Memory) Remote Code Execution. + +Fix Effectiveness +-------------------------------------------------------------------- +The parameter layout now matches all call-sites, preventing stack +corruption. All local variables are explicitly initialised so no stale +pointers can escape the function. No obvious alternative path to reach +freed memory remains, therefore the patch fully resolves the UAF. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58739_explorer.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58739_explorer.exe.txt new file mode 100644 index 0000000..a1a34f9 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58739_explorer.exe.txt @@ -0,0 +1,127 @@ +{'file': 'explorer.exe', 'patch_store_uid': '3165bbac-c206-46c7-bab4-8964399b9793', 'confidence': 0.15, 'kb': 'KB5066835', 'cve': 'CVE-2025-58739', 'change_count': 128, 'date': 1763406223.9952266} +-------------------------------------------------------------------- +CVE-2025-58739 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows 10/11 File Explorer (explorer.exe) – C++17 +std::filesystem helper routine used when Explorer extracts the +filename part of a user-supplied path. + +Vulnerability Class +-------------------------------------------------------------------- +Uninitialised-variable usage leading to out-of-bounds read and +information disclosure (CWE-200). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine is the compiler-generated wrapper for +std::filesystem::path::filename() inside explorer.exe. Its purpose is +to build a new std::filesystem::path that contains only the filename +component of an existing std::wstring path. + +Pseudo-register layout before the fix: + a1 – address of std::filesystem::path (input) + a2 – address of std::filesystem::path (output) + +Local variables (before patch) + v4 const unsigned short *end_ptr (initialised) + v5 const unsigned short *begin_ptr (UNINITIALISED) + +Key sequence: + 1. std::wstring::operator basic_string_view() returns a view of the + underlying wchar buffer (v3). + 2. v4 is set to the address after the last character in that buffer. + 3. v5 is never initialised. + 4. _Find_filename( *path, v4, v5 ) is executed. + +std::filesystem::_Find_filename(begin,end) expects its third parameter +(begin) to point inside the same buffer as ‘end’. Because v5 contains +stack garbage, the helper scans memory outside of the legitimate +wstring. + +After _Find_filename() returns, the function computes + + v7[1] = (end_ptr - returned_ptr) >> 1; + +and constructs a new std::filesystem::path from that pair. Whatever +memory range was walked by _Find_filename() becomes observable via the +new path object that Explorer may later log, compare, or expose through +IPC. Thus random portions of stack/heap memory can be disclosed to an +unprivileged actor. + +The bug is entirely due to an uninitialised pointer; no bounds checks +exist to detect the bad state. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – explorer!std::filesystem::path::filename +v3 = std::wstring::operator basic_string_view<unsigned short>(a1, v8); +v4 = (const unsigned __int16 *)(*(_QWORD *)v3 + + 2i64 * *(_QWORD *)(v3 + 8)); // end of string +// v5 is never written +v7[0] = (__int64)std::filesystem::_Find_filename(*(std::filesystem **)v3, + v4, v5); // UB! +``` + +```c +// after patch – new implementation no longer calls _Find_filename +*(_OWORD *)a2 = 0; +a2[3] = 7; // initialise result string +std::basic_stringbuf<unsigned short>::_Get_buffer_view(a1+24,&v4); +if (v4) + std::wstring::assign(a2); // safe copy +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Explorer enumerates a file system object (local path or UNC share). +2. It calls std::filesystem::path::filename() to extract the last + component. +3. The uninitialised v5 is forwarded into _Find_filename(). +4. _Find_filename() walks attacker-controlled memory addresses, + copies bytes into a new std::wstring. +5. Resulting string may be surfaced via the UI, shell namespace + extension callbacks, logs, or over the network (spoofing / info + leak). + +Attack Vector +-------------------------------------------------------------------- +A remote attacker can place a specially crafted file or directory name +on a SMB/HTTP WebDAV share and lure a victim into browsing that +location with Explorer. During path parsing the bug is triggered, +causing disclosure of stack/heap data that can be collected through a +side channel (e.g., reflected in the shell window title, tooltip, or +COM IPC message), enabling spoofing of trusted UI elements. + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the entire implementation with a safe wrapper: +1. The output path object is fully zero-initialised. +2. The helper _Get_buffer_view() returns a validated view of the source + buffer, eliminating manual pointer math. +3. Only std::wstring::assign() is used to copy data, so no direct + pointer arithmetic or custom find routine is needed. +4. The uninitialised variable ‘v5’ and the call to _Find_filename() are + removed, eliminating the out-of-bounds read. + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could disclose arbitrary portions of process +memory, potentially including NTLM hashes, window handles, or other +sensitive information, and craft misleading filenames to spoof UI +state. The bug therefore maps to CWE-200 and is addressed in +Microsoft advisory CVE-2025-58739 (Windows File Explorer Spoofing +Vulnerability). + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code no longer passes uncontrolled pointers to +_Find_filename(); all variables are initialised and bounds are +maintained by the C++ standard library. No equivalent pathways using +_Find_filename() remain in the modified function, so the specific +information-disclosure vector is closed. A broader audit of other +filesystem helpers is still advisable but this change appears +sufficient for the identified root cause. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58739_shell32.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58739_shell32.dll.txt new file mode 100644 index 0000000..e12ebc7 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-58739_shell32.dll.txt @@ -0,0 +1,113 @@ +{'cve': 'CVE-2025-58739', 'kb': 'KB5066835', 'change_count': 296, 'file': 'shell32.dll', 'date': 1763406162.9708438, 'confidence': 0.13, 'patch_store_uid': 'c042dbb5-0201-458e-b362-93dacc33b254'} +-------------------------------------------------------------------- +CVE-2025-58739 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Shell component (shell32.dll) – routine +CAppPathReader::_PathToAppPathKeyHandle that translates a file path +into an "App Paths" registry key and opens the key for reading. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / stack-based buffer overflow (CWE-121). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the function prototype was + + int _PathToAppPathKeyHandle(CAppPathReader *this, + unsigned short *Path, + HKEY *OutKey, + unsigned int cchBuf) + +It allocated a 320-element WCHAR array on the stack (SubKey) and then +called the helper _PathToAppPathKeyBase to build the final registry +subkey. The helper received the caller-supplied length (cchBuf), but +no subsequent check was performed in _PathToAppPathKeyHandle to verify +that SubKey remained inside its bounds or was NULL-terminated. After +construction the routine immediately passed SubKey to RegOpenKeyExW. + +Because SubKey lives on the stack, any pathname longer than 319 +characters (or one that causes _PathToAppPathKeyBase to append “.exe” +without room for it) writes past the end of the local buffer and +trashes adjacent stack variables, including the saved return address. +An attacker who can control the file name processed by +CAppPathReader (e.g. via a crafted shortcut or network share +placement) can therefore corrupt memory inside explorer.exe. + +Additional type safety issues existed: the second parameter was typed +as an UTF-16 string but was later cast to an HKEY when calling +RegOpenKeyExW, resulting in type confusion. This allowed arbitrary +pointer values to be interpreted as registry handles, unintentionally +leaking registry data if the value happened to equal a predefined +handle constant (0x80000000 – 0x80000006). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old implementation (simplified) +int __fastcall _PathToAppPathKeyHandle(..., + unsigned __int16 *Path, + HKEY *hKeyOut, + unsigned int cch) +{ + WCHAR SubKey[320]; // fixed-size stack buffer + + // Helper builds "App Paths" subkey; no result length checked + _PathToAppPathKeyBase(this, Path, SubKey, cch); + + // Path pointer is mis-cast to HKEY + return RegOpenKeyExW((HKEY)Path, SubKey, 0, 0x20019, hKeyOut); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User browses to or opens a file whose name length > 319 chars or is + crafted to defeat internal assumptions. +2. Explorer invokes CAppPathReader to resolve an AppPath entry. +3. _PathToAppPathKeyBase copies the long file name plus “.exe” into the + 320-WCHAR SubKey buffer. +4. SubKey overflows, corrupting stack. +5. The corrupted return address leads to code execution or information + disclosure when the function returns. + +Attack Vector +-------------------------------------------------------------------- +Any context that causes Explorer.exe to process an attacker-controlled +file name – for example a malicious LNK located on a remote SMB share, +a ZIP file preview, or a downloaded folder containing overlong file +names – can reach the vulnerable routine without user confirmation. +No special privileges are required. + +Patch Description +-------------------------------------------------------------------- +The new implementation completely replaces the helper and builds the +subkey using safe routines: +• Uses StringCchPrintfW with an explicit 314-character limit. +• Verifies that the generated string has no extension, then manually + appends “.exe” only after checking available space. +• Returns ERROR_INSUFFICIENT_BUFFER (0x8007007A) or + ERROR_INVALID_PARAMETER (0x80070057) instead of writing past the + array. +• The function signature is corrected: the second argument is now an + HKEY, removing the earlier type confusion. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could achieve arbitrary code execution in +explorer.exe (current user context) or read registry information by +leveraging the type confusion. Because explorer.exe is always loaded +in a logon session, exploitation provides a stable privilege-escalation +or information-disclosure primitive. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch introduces deterministic bounds checks, constant-sized +buffers, correct error propagation, and eliminates the pointer type +confusion; therefore the original overflow and spoofing conditions are +no longer reachable. No residual paths that write to SubKey without a +length check remain in the updated code, indicating the fix is +adequate. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59187_ntoskrnl.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59187_ntoskrnl.exe.txt new file mode 100644 index 0000000..340e80a --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59187_ntoskrnl.exe.txt @@ -0,0 +1,113 @@ +{'date': 1763403252.9752977, 'cve': 'CVE-2025-59187', 'change_count': 222, 'file': 'ntoskrnl.exe', 'kb': 'KB5066835', 'confidence': 0.12, 'patch_store_uid': 'c1b97b38-6328-4043-8cf8-12e6eafee863'} +-------------------------------------------------------------------- +CVE-2025-59187 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NT kernel (ntoskrnl.exe) – Plug-and-Play property helper +routine PnpValidatePropertyData used when user mode asks the +kernel to set or query a device property. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / out-of-bounds kernel memory access +(CWE-20, CWE-822). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The routine PnpValidatePropertyData is responsible for making sure +that caller-supplied property data matches the DEVPROP_TYPE that is +encoded in the low 16 bits of the a3 argument. For properties that +represent a Unicode string or a list of Unicode strings the previous +implementation performed its own length checks: + +1. (v8 & 0x2000) == 0 – single string +2. (v8 & 0x2000) != 0 – MULTI_SZ (string list) + +For both cases the code walked the buffer byte-by-byte looking for +a terminating WCHAR 0x0000 and calculated how many bytes were +"consumed" (v19 / v22). The manually written loops had two major +flaws: + +• The loop dereferenced *(_WORD *)SecurityDescriptor without first + making sure that the word lies inside the caller-supplied buffer; + if the final NUL was missing the pointer marched past the end of + the validated region and read uncontrolled kernel memory. + +• Size bookkeeping used 32-bit unsigned arithmetic (v23, v24, v26, + v27) so the running counter could wrap, letting the final + comparison "v19 == v4" succeed even though the pointer was already + past the trusted area. + +Because the buffer is still mapped from user space, a local attacker +can supply a short, non-terminated string that straddles a valid +user page followed by an unmapped page: when the kernel continues +reading beyond the end of the user allocation, a controlled page +fault is raised in kernel context, allowing elevation via the usual +exception-handling techniques. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – custom scan of MULTI_SZ +while ( *(_WORD *)SecurityDescriptor ) { + ... // length bookkeeping + SecurityDescriptor += 2 * (v28 >> 1); // advances pointer +} +// no guarantee pointer is still inside [buf, buf+Length) +``` + +```c +// after patch – safe helper that never crosses buffer limit +while ( *(_WORD *)psz ) { + if ( RtlStringCbLengthW((STRSAFE_PCNZWCH)psz, + v4 - v20, + &pcbLength) < 0 ) + return STATUS_INVALID_PARAMETER; + ... +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User mode (e.g. SetupDiSetDeviceProperty) -> + CM_Set_DevNode_Property_Ex -> + nt!NtSetValueKey -> + nt!PnpSetDevicePropertyData -> + nt!PnpValidatePropertyData (vulnerable) -> + unchecked read past caller buffer triggers kernel access fault. + +Attack Vector +-------------------------------------------------------------------- +Any local, non-privileged process that has the standard +DEVICE_QUERY/DEVICE_SET property privileges can pass a crafted +property blob to the Plug-and-Play manager. By omitting the final +Unicode NUL in a DEVPROP_TYPE_STRING or STRING_LIST value the +attacker forces the kernel to read beyond the supplied buffer. + +Patch Description +-------------------------------------------------------------------- +1. Function prototype changed from char* to __int64* so that the + compiler treats the parameter as properly aligned pointer. +2. All manual scanning logic replaced by RtlStringCbLengthW, which + receives the remaining buffer size and guarantees the read stays + within bounds. +3. Length accounting now done with size_t/unsigned __int64 variables + (pcbLength, v20, v21, v22) eliminating 32-bit wrap. +4. Additional short-circuit checks were added before loops and after + each RtlStringCbLengthW call to reject any error immediately. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a user-supplied property value could cause an +out-of-bounds read from kernel mode, leading to a controlled +kernel-mode access fault. By preparing the faulting address an +attacker can execute code in kernel context and elevate privileges. + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation uses well-tested string helpers that respect +buffer length, performs 64-bit size arithmetic, and validates the +result of every helper call. There are no remaining unbounded +pointer walks, so the original over-read condition is closed. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59189_bfs.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59189_bfs.sys.txt new file mode 100644 index 0000000..72452f3 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59189_bfs.sys.txt @@ -0,0 +1,147 @@ +{'date': 1763402991.0459373, 'patch_store_uid': 'e256c23f-dcdb-48c3-81b0-6c892c4702ec', 'kb': 'KB5066835', 'file': 'bfs.sys', 'confidence': 0.22, 'change_count': 14, 'cve': 'CVE-2025-59189'} +-------------------------------------------------------------------- +CVE-2025-59189 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Brokering File System kernel driver (bfs.sys), numerous +callback entry points such as BfsIdleCheckWorkitemRoutine, +BfsPreCreatePipeOperation / BfsPreCreateOperation, +BfsRegistry{Pre,Post}CreateCallback and the corresponding *Post* paths. + + +Vulnerability Class +-------------------------------------------------------------------- +Use After Free – CWE-416 + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The driver maintains several global objects that are lazily destroyed +when the driver is torn down or when policy state changes: + + • gBfsRundownProtection – EX_RUNDOWN_REF used to guard global data + • gBfsGlobalFileTable – RTL_DYNAMIC_HASH_TABLE containing policy + entries + • gBfsPipeMappingTable – mapping table for named pipes + +Before the patch many call-backs accessed those objects without holding +an active rundown reference or released the reference too early. The +most visible paths were: + + 1. BfsIdleCheckWorkitemRoutine() + – called in a worker thread that frees policy structures. + 2. I/O pre-operation callbacks (BfsPreCreatePipeOperation, + BfsPreCreateOperation) that run in the context of the calling user + thread and dereference the same tables. + 3. Registry filter callbacks (BfsRegistryPreCreateCallback / + BfsRegistryPostCreateCallback). + 4. Post-operation paths that free allocations and then continue to + touch the global tables. + +Because there was no reliable ExAcquireRundownProtection() around those +critical sections, the following race was possible: + + Thread-A (idle work-item) + ExAcquirePushLockExclusive() + // free policy hash entries + ExFreePool(..) + ExReleasePushLockExclusive() + + Thread-B (CreateFile on protected path) + uses pointer retrieved from gBfsGlobalFileTable -> **freed pool** + => pool-use-after-free in kernel mode. + +Depending on heap layout the attacker can obtain arbitrary kernel pool +write primitives and elevate privileges locally. + +A related lifetime bug existed in PostCreate* code paths: memory was +freed (ExFreePoolWithTag) and only afterwards was the rundown reference +released, giving a window in which a new thread could reuse the freed +chunk while the old pointer was still reachable. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (BfsIdleCheckWorkitemRoutine) +KeEnterCriticalRegion(); +if (ExAcquireRundownProtection(&gBfsRundownProtection)) +{ + BfsCheckAndReleaseIdlePolicy(Context); // may free globals + ExReleaseRundownProtection(&gBfsRundownProtection); +} +KeLeaveCriticalRegion(); +IoFreeWorkItem(IoWorkItem); // rundown dropped *after* + // free ; other paths had + // no rundown at all +``` +```c +// patched +v5 = ExAcquireRundownProtection(&gBfsRundownProtection); +if (!Feature_IsEnabled() ) { // gate for hot-patch roll-out + if (!v5) goto leave; +} +... +BfsCheckAndReleaseIdlePolicy(Context); +if (Feature_IsEnabled()) KeEnterCriticalRegion(); +ExReleaseRundownProtection(&gBfsRundownProtection); +``` +Similar rundown-acquire / release blocks were added to every touched +callback and all error/early-return paths. +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens / creates many files, pipes or registry keys to force + bfs.sys into its callback paths. +2. In parallel the attacker causes the idle work-item to run (power- + transition, filter unload, Feature flag toggle) which frees policy + tables. +3. Lack of proper rundown in the I/O callback lets it dereference a + pointer that has just been freed. +4. Crafted pool layout gives attacker control of freed chunk -> kernel + memory corruption and privilege escalation. + + +Attack Vector +-------------------------------------------------------------------- +Local, unprivileged user code. No special privileges are required – +issued NtCreateFile / NtCreateNamedPipe or RegCreateKeyEx calls while +another thread triggers the policy idle path. + + +Patch Description +-------------------------------------------------------------------- +1. Systematically adds ExAcquireRundownProtection(&gBfsRundownProtection) + at the start of every entry point that touches global state, and + guarantees ExReleaseRundownProtection on every exit path, including + all error jumps. +2. Where hash tables are modified, a complementary push-lock discipline + (ExAcquirePushLock{Shared,Exclusive}Ex) is now employed. +3. Rundown release is performed *before* freeing pool memory, removing + the dangling-pointer window. +4. ETW logging constants updated; debug logging now uses new + (&unk_140015C91) GUID, signalling code churn only. +5. New Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage* gates allow + Microsoft to enable or disable the hardened path at run-time. + + +Security Impact +-------------------------------------------------------------------- +The bug permitted kernel-mode use-after-free, enabling local attackers +to execute arbitrary code in kernel context or crash the system. +Microsoft rates the issue as Elevation of Privilege (EoP). + + +Fix Effectiveness +-------------------------------------------------------------------- +All observed code paths now hold a rundown reference for the entire +lifetime of pointers retrieved from shared global tables, and release +that reference before the memory is returned to the pool. Provided all +entry/exit paths are covered – which the diff suggests – the fix fully +mitigates the UAF condition. No residual unsafe dereferences were +observed in the patched routines. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_mssrch.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_mssrch.dll.txt new file mode 100644 index 0000000..86075f5 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_mssrch.dll.txt @@ -0,0 +1,103 @@ +{'kb': 'KB5066835', 'date': 1763406131.7154503, 'confidence': 0.22, 'change_count': 64, 'file': 'mssrch.dll', 'cve': 'CVE-2025-59190', 'patch_store_uid': '6a66212c-b557-4770-bf02-18bf20cc194a'} +-------------------------------------------------------------------- +CVE-2025-59190 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Search Service (mssrch.dll). Affected code is the +priority-queue bookkeeping inside CPriorityQueue::MarkAllPassesCompleted +ForIndexedItem, part of the indexing pipeline that tracks which +processing passes have already been completed for an item. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation leading to out-of-bounds write / denial of +service (CWE-20 / CWE-787). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The old implementation of +CPriorityQueue::MarkAllPassesCompletedForIndexedItem(this,UINT index) +manually manages a two-level sparse bitset. The algorithm divides the +32-bit caller-supplied index into a page number (v4) and an in-page bit +position: + + page = index >> this->PageShift (DWORD read at *(this+133)+20) + bitPos = index & 0x3F (low 6 bits) + wordIx = PageMask & (index >> 6) (mask value read at *(bitset)+16) + +No range validation is performed on wordIx before it is multiplied by +8 and added to the page base pointer: + + qword *word = *(pageBase + 8*wordIx); + +If the caller supplies an index that, after the mask operation, yields +a wordIx that is larger than the number of qwords actually allocated in +the page, the calculation walks past the end of the 8-byte-aligned page +buffer. Subsequent read-modify-write of the target bit corrupts +adjacent memory and commonly raises an access-violation that terminates +the Search service process. + +Because the public Search APIs allow unprivileged local code to trigger +re-indexing of arbitrary user-controlled items, a local attacker can +feed a crafted item identifier with a large numeric key to force the +out-of-range index. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old code (simplified) +unsigned __int64 page = a2 >> *(_DWORD *)(*((_QWORD *)this + 133) + 0x14); +... +unsigned __int64 wordIx = *(unsigned int *)(*(_QWORD *)v2 + 0x10) & (a2 >> 6); +__int64 oldBits = *(_QWORD *)(*pagePtr + 8*wordIx); // OOB if wordIx + // too big +if ((oldBits & (1ULL << bitPos)) == 0) + *(_QWORD *)(*pagePtr + 8*wordIx) = oldBits | (1ULL << bitPos); +``` +The variable wordIx is never checked against the real length of the +allocated page (v7[6] qwords). + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-supplied item added to the indexer. +2. Indexer calls CPriorityQueue::MarkAllPassesCompletedForIndexedItem. +3. Function derives page/word/bit without validating wordIx. +4. Memory past the allocated bitset page is accessed. +5. Access violation propagates, causing SearchIndexer.exe crash. + +Attack Vector +-------------------------------------------------------------------- +A local, unprivileged attacker issues Search API calls (e.g. IRowset, +ISearchCatalogManager, or by placing specially crafted metadata into NTFS +alternate data streams) that force the indexer to process an item with a +large 32-bit ID. When the indexer marks that item as having completed a +pass, the out-of-bounds write is triggered and the Search service +terminates, resulting in denial of service for all users on the machine. + +Patch Description +-------------------------------------------------------------------- +The vulnerable routine was deleted entirely. A new method +CTransactionHashEntry::RequiredOriginalTransactionPasses simply delegates +processing to TransactionPass::LoadFromTransactionExtendedFlags, which +returns a constant list of required passes. No manual sparse bit +management remains, removing the faulty index calculations and the +entire attack surface. + +Security Impact +-------------------------------------------------------------------- +Before the patch, malformed indices could crash SearchIndexer.exe, +disrupting desktop, Outlook and Cortana search. Because the memory +corruption occurs in a write operation, exploitation for elevation of +privilege cannot be ruled out, but no evidence of such exploitation is +known. The primary, confirmed impact is denial of service. + +Fix Effectiveness +-------------------------------------------------------------------- +The dangerous code path has been replaced, eliminating the unchecked +word index arithmetic. Provided that +TransactionPass::LoadFromTransactionExtendedFlags does not re-introduce +similar unchecked array operations, the patch fully mitigates the issue. +Regression tests around extremely large item IDs are recommended to +confirm there are no residual edge cases. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchfilterhost.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchfilterhost.exe.txt new file mode 100644 index 0000000..a9ecefc --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchfilterhost.exe.txt @@ -0,0 +1,157 @@ +{'kb': 'KB5066835', 'date': 1763406151.5496657, 'confidence': 0.12, 'change_count': 53, 'file': 'searchfilterhost.exe', 'cve': 'CVE-2025-59190', 'patch_store_uid': 'bbd8c9d6-7db5-4474-9b79-39b862387365'} +-------------------------------------------------------------------- +CVE-2025-59190 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Search Service – user-mode filter pipeline contained in +searchfilterhost.exe. The affected code lives in the +SemanticTextFilterAdapter and TransactionFilterAdapter helper +classes that wrap concrete IFilter-style plug-ins. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation (CWE-20) leading to NULL-pointer +Dereference / Denial-of-Service. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The adapters expose thin wrappers (BreakType and Locale) that +forward the request to one of two internal filter interfaces that +are stored in the object at + +48 -> filter for document scope + +56 -> filter for property scope +The choice is controlled by an optional enum stored at +80 (for +Transaction* adapters) or +220 (for Semantic* adapters): + enum tagGenericFilterItemType { None = 0, Property = 1, + Break = 4, ... } + +PRE-PATCH (TransactionFilterAdapter::BreakType / ::Locale) +----------------------------------------------------------- +The code only treated values 0 and 1 as valid: + if (type == 0) use pointer +48 + else if (type == 1) use pointer +56 + else return E_INVALIDARG + +When the value was 4 (Break) the logic fell into the final +else-branch, returned failure 0x8000000E, but **left the adapter in +an inconsistent state**. Subsequent callers expected the request +to be serviced and attempted again, eventually dereferencing the +still-null delegate pointers, crashing searchfilterhost.exe and +resetting the Windows Search Service. + +PRE-PATCH (SemanticTextFilterAdapter::BreakType) +------------------------------------------------ +The Semantic variant already accepted 4, but a missing defensive +check meant that the filter interface could be dereferenced before +its presence was verified. + +PATCH +----- +The fix unifies the validation in all three affected methods: + 1. The enum is loaded once (rsi) and compared. + 2. (type == 0 || type == 4) => delegate +48 + 3. (type == 1) => delegate +56 + 4. otherwise => return E_INVALIDARG + 5. Before invoking the vtable, the pointer is checked for NULL. + +Additional non-security code (feature-flagged telemetry and cache +population) was inserted in SemanticTextFilterAdapter::BreakType, +but the security hardening consists solely of the stricter enum +handling and repeat NULL checks. + +Thus the root cause was an incomplete input validation state +machine that did not regard tagGenericFilterItemType::Break as a +legal value, eventually allowing a NULL-pointer dereference and +service crash. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// TransactionFilterAdapter::BreakType (before) +if (!*(_DWORD *)optional) { + v = *(QWORD *)(a1+48); + return v->BreakType(a2); +} +if (*(_DWORD *)optional == 1) { + v = *(QWORD *)(a1+56); + return v->BreakType(a2); +} +*a2 = 0; // type 2,3,4 fall here => wrong state +return 0x8000000E; +``` + +```c +// TransactionFilterAdapter::BreakType (after) +opt = a1+80; +if (!val(opt) || val(opt) == 4) { + v = *(QWORD *)(a1+48); + if (!v) return 0x8000033; + return v->BreakType(a2); +} +if (val(opt) == 1) { + v = *(QWORD *)(a1+56); + if (!v) return 0x8000033; + return v->BreakType(a2); +} +*a2 = 0; +return 0x8000007E; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User provides specially crafted data to the Windows Search index + (e.g., a document with embedded property of type 4). +2. The indexer hands the stream to searchfilterhost.exe. +3. TransactionFilterAdapter::BreakType / ::Locale is invoked. +4. Pre-patch code treats type==4 as illegal, returns error but leaves + internal state unchanged. +5. Higher-level logic retries or continues and eventually calls the + same function again, which now dereferences a NULL delegate + pointer at +48, raising an access-violation. +6. The unhandled exception terminates searchfilterhost.exe, + temporarily disabling the Windows Search Service. + + +Attack Vector +-------------------------------------------------------------------- +Local, non-privileged attacker can supply content that is parsed by +Windows Search (e.g., by indexing a crafted document or archive). +No special permissions are required beyond the ability to add files +to a location that is indexed by the service. + + +Patch Description +-------------------------------------------------------------------- +• Adds consistent validation that accepts enum values 0 and 4. +• Refactors the decision logic to a single block using a local copy + of the optional value, eliminating duplicate reads. +• Introduces explicit NULL checks for both delegate interface + pointers before each virtual call. +• Semantic adapter received identical hardening plus diagnostic and + cache code guarded by a WIL feature flag. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, malformed input could crash searchfilterhost.exe, +causing a service restart loop and rendering Windows Search +unavailable for the user session. No privilege escalation or data +corruption was observed; the impact is denial-of-service. + + +Fix Effectiveness +-------------------------------------------------------------------- +The hardened logic ensures that: + 1. All legal enum values (0,1,4) are handled explicitly. + 2. Illegal values immediately translate to an error code without + touching delegate pointers. + 3. Any delegate pointer is validated for NULL before use. +Hence a NULL-pointer dereference can no longer be triggered through +malformed GenericFilterItemType values, effectively mitigating the +reported DoS condition. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchindexer.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchindexer.exe.txt new file mode 100644 index 0000000..ca2eb07 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchindexer.exe.txt @@ -0,0 +1,100 @@ +{'kb': 'KB5066835', 'confidence': 0.14, 'change_count': 15, 'date': 1763406161.8281648, 'patch_store_uid': 'dda3686d-f7f1-43d7-8d70-2a4077935f54', 'cve': 'CVE-2025-59190', 'file': 'searchindexer.exe'} +-------------------------------------------------------------------- +CVE-2025-59190 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Search Service (searchindexer.exe). Affected routine +is the internal helper now named sub_140081D30 (formerly sub_140080F80) +invoked when resolving an indexer version string for a given catalog +name. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation / Use-of-Uninitialised Pointer leading to +NULL/invalid-pointer dereference (Denial-of-Service). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The function is called with three parameters: + a1 : HKEY registry handle (service context) + a2 : const WCHAR* CatalogName (user controlled) + a3 : __int64* OutVersionHandle + +Before the patch the code attempted to block the reserved catalog names +"Windows" and "SystemIndex" with the following check: + + _wcsnicmp(v6, L"Windows", 7) + _wcsnicmp(a2, L"SystemIndex", 11) + +The variable v6 is never initialised prior to this call; it contains a +stack garbage value. When the execution reaches _wcsnicmp the garbage +pointer is used as the first UNICODE_STRING, causing the CRT routine to +read arbitrary memory. If v6 is not a valid user-mode pointer the +process raises an access-violation exception and Windows Search Service +terminates. Because the service runs inside the SCM service host, the +crash results in an immediate stop/restart cycle – a local denial of +service condition. + +The bug is reachable because a2 (CatalogName) can be supplied through +public COM interfaces (ISearchCatalogManager::GetCatalog), the Windows +Search protocol handler, or via PowerShell/Explorer search APIs. No +special privileges are required. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if (!(_wcsnicmp(v6, L"Windows", 7) || + _wcsnicmp(a2, L"SystemIndex", 11))) { ... } + +// AFTER +if (!(_wcsnicmp(a2, L"Windows", 7) || + _wcsnicmp(a2, L"SystemIndex", 11))) { ... } +``` +The sole change is the replacement of the uninitialised variable v6 by +the legitimate parameter a2. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls a public Windows Search API providing an arbitrary + catalog name. +2. API forwards to searchindexer.exe, eventually reaching + sub_140080F80. +3. Function compares catalog name to reserved names using uninitialised + pointer v6 -> invalid memory read. +4. Access violation exception bubbles up; searchindexer.exe terminates; + the SCM restarts it, denying service during the crash loop. + +Attack Vector +-------------------------------------------------------------------- +Local, unauthenticated. Any user able to invoke Windows Search APIs +(COM, WSearch indexer control utility, PowerShell, or shell extensions) +can supply a crafted request and crash the service. + +Patch Description +-------------------------------------------------------------------- +1. Replaced the first argument of _wcsnicmp from v6 to the validated + catalog name pointer a2 (correct variable). +2. Added feature-flagged call to sub_1400AA1D8 to sanity-check the + catalog name before any further processing; returns + ERROR_INVALID_PARAMETER on failure. +3. Hardened error handling paths and updated global logging/telemetry + symbols (unk_1400EAD70, byte_1400EAAC1). + +Security Impact +-------------------------------------------------------------------- +Prior to the fix any local user could repeatedly crash the Windows +Search Service, resulting in loss of indexing functionality and +continual service restart attempts (denial of service). No elevation +of privilege or information disclosure is implied, but availability is +severely affected. + +Fix Effectiveness +-------------------------------------------------------------------- +The variable misuse has been completely removed; the comparison now +always receives a valid, caller-supplied pointer. Additional upfront +validation further guards against malformed catalog names. Because the +faulting code path is eliminated and new validation is present, the fix +is considered effective against the described DoS condition. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchprotocolhost.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchprotocolhost.exe.txt new file mode 100644 index 0000000..1b9e31d --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59190_searchprotocolhost.exe.txt @@ -0,0 +1,108 @@ +{'confidence': 0.3, 'change_count': 38, 'cve': 'CVE-2025-59190', 'date': 1763406209.3867717, 'kb': 'KB5066835', 'patch_store_uid': '4d862698-3c26-4890-a42d-1e3fbd91ca45', 'file': 'searchprotocolhost.exe'} +-------------------------------------------------------------------- +CVE-2025-59190 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Search Service – image-embedding helper +(SearchProtocolHost.exe, CImageHandler::ConvertPBGRA8ToBGRA8Buffer). + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation / Out-of-bounds read (CWE-20 → DoS). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper routine +CImageHandler::ConvertPBGRA8ToBGRA8Buffer(IMAGE_INFO const&, UCHAR*) +converts pre-multiplied BGRA pixel data. When the supplied IMAGE_INFO +structure advertises an unsupported pixel-format ( `imageInfo.PixelFmt +!= 1` ), the legacy code enters an error path that constructs a WinRT +`hstring` for the exception message: + + winrt::param::hstring bad(a2); // a2 == &IMAGE_INFO !!! + +`a2` is a pointer to the caller-supplied IMAGE_INFO, *not* a wide-char +string. `winrt::param::hstring` walks memory byte-by-byte until it +finds a terminating L"\0", so it starts reading past the structure into +arbitrary process memory. If the buffer ends in an unmapped page the +SearchProtocolHost service crashes with an access-violation, denying +service to consumers of Windows Search. + +Structures / fields involved: + IMAGE_INFO + +0 DWORD Width + +4 DWORD Height + +8 DWORD PixelFormat (expected 1 == PBGRA8) + +Execution parameters: + if PixelFormat != 1 → unsupported → malformed call above. + +The crash is purely a read; no write or privilege-gain is involved, but +it is sufficient to continually terminate the service. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +if (imageInfo.PixelFmt != 1) { + winrt::param::hstring h((const wchar_t*)imageInfo); // OOB read + winrt::hresult_invalid_argument hr(h, loc); + throw hr; +} + +// After (23H2 patch) +if (imageInfo.PixelFmt != 1) { + winrt::param::hstring h(L"non-supported pixel format passed"); + winrt::hresult_invalid_argument hr(h, loc); + throw hr; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client code feeds an IMAGE_INFO with `PixelFormat != 1` to the + Search image pipeline. +2. ConvertPBGRA8ToBGRA8Buffer() hits the “unsupported” branch. +3. Old build passes the structure pointer to hstring → linear scan + beyond valid memory → AV → SearchProtocolHost.exe terminates. + + +Attack Vector +-------------------------------------------------------------------- +Local, unauthenticated. Any process that can submit crafted thumbnail +or preview data to the Windows Search indexer (e.g. via IFilter/IStore) +can feed the malformed IMAGE_INFO and crash the service repeatedly, +causing a denial-of-service condition. + + +Patch Description +-------------------------------------------------------------------- +1. The error path now builds the hstring from a *static, literal* wide + string, eliminating the incorrect cast. +2. The winrt::param::hstring constructor was hardened: it now counts + characters, verifies a terminating NUL, and aborts when given an + unterminated buffer. +3. Minor related clean-ups (modernised exception helper, etc.) + + +Security Impact +-------------------------------------------------------------------- +• Prior to the patch, a malformed request leads to an access-violation + in SearchProtocolHost, stopping Windows Search. +• No EoP or info-leak; the issue is a service Denial-of-Service. + + +Fix Effectiveness +-------------------------------------------------------------------- +The literal string removes the possibility of dereferencing attacker +input. Additional validation in the hstring constructor adds a second +layer of defence for any remaining call-sites. No residual paths to +the original flaw were observed in the patched binary. + +-------------------------------------------------------------------- diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59191_cdpsvc.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59191_cdpsvc.dll.txt new file mode 100644 index 0000000..7a9a06b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59191_cdpsvc.dll.txt @@ -0,0 +1,110 @@ +{'date': 1763402991.8247757, 'confidence': 0.36, 'change_count': 8, 'kb': 'KB5066835', 'patch_store_uid': '0e54175f-9f22-44cd-8cdd-4e0fede69033', 'cve': 'CVE-2025-59191', 'file': 'cdpsvc.dll'} +-------------------------------------------------------------------- +CVE-2025-59191 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Connected Devices Platform Service (cdpsvc.dll) – routine +lambda_7ffd25213cfe03b0af8e887eb5338eb1_::operator(), invoked while +converting CDP application identifiers. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The lambda operator receives two structures: + a1 -> input descriptor + +0x00 QWORD **AppIdList + +0x08 WORD *AppIdCountPtr (number of app IDs) + +0x10 QWORD **AppIdStrings (array of char*) + a2 -> output descriptor + +0x00 NTSTATUS Status + +0x08 ... (other fields) + +0x40 QWORD PtrArray (pointer array allocated here) + +0x48 WORD OutCount + +0x50 ... + +Before the patch the code allocated memory for PtrArray with + + size = *AppIdCountPtr; // WORD count + PtrArray = CoTaskMemAlloc(size); // bytes, **NOT** count*8 + +Later it stored an 8-byte pointer for every element: + + PtrArray[count_index] = CoTaskMemAlloc(len); + +Because only <count> bytes were reserved while <count>*8 bytes are +written, the allocator’s bookkeeping region and adjacent heap objects +are overwritten whenever AppIdCount > 1. The write primitive occurs in +a SYSTEM service context, enabling controlled memory corruption. + +Patch code introduces a size calculation that multiplies the element +count by 8 (sizeof(QWORD)) when feature 2578215227 is enabled: + + bytes = count; + if (FeatureEnabled) + bytes *= 8; + PtrArray = CoTaskMemAlloc(bytes); + +Thus the allocated buffer now matches the number of 64-bit entries to be +written, removing the overflow. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable allocation (before) +size_t bytes = **(unsigned __int16 **)(a1 + 8); // WORD count +*(_QWORD *)(a2 + 64) = CoTaskMemAlloc(bytes); // too small + +// fixed allocation (after) +SIZE_T bytes = **(unsigned __int16 **)(a1 + 8); +if (wil::FeatureEnabled(...)) // always true on patched systems + bytes *= 8; // adjust for QWORD array +*(_QWORD *)(a2 + 64) = CoTaskMemAlloc(bytes); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged client hands a crafted CDP AppId list to cdpsvc via an + RPC/ALPC surface (exact message unknown). +2. Service calls ComAppIdFromCDPAppId_0(), which in turn invokes the + lambda operator. +3. Lambda determines AppIdCount, allocates undersized buffer, then writes + pointers past its end while copying each string. +4. Heap metadata is corrupted; attacker can follow up with additional + allocations/frees to achieve arbitrary code execution in the service + process (NT AUTHORITY\SYSTEM). + +Attack Vector +-------------------------------------------------------------------- +Local. An authenticated user sends a malformed request to the Connected +Devices Platform Service, supplying an AppIdCount greater than one and +controlled strings. No special privileges are required beyond the +ability to communicate with cdpsvc. + +Patch Description +-------------------------------------------------------------------- +The fix recalculates the allocation size for the pointer array: + • Retrieves WORD AppIdCount (v7). + • If feature flag 2578215227 is active, multiplies by 8 to account for + sizeof(QWORD). + • Abort path added if allocation fails (goto LABEL_11). +Nothing else changes; copy loop logic is untouched. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a local attacker could trigger a heap buffer +overflow in a SYSTEM service, leading to elevation of privilege or +potentially full remote-code execution within the service process. + +Fix Effectiveness +-------------------------------------------------------------------- +Allocating count*8 bytes aligns the buffer size with written data, +removing the direct overflow condition. Protection relies on the +feature flag being enabled system-wide; if it were disabled (unknown +configuration), the old behavior would persist. Apart from that +contingency, the patch fully resolves the described flaw. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59192_storport.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59192_storport.sys.txt new file mode 100644 index 0000000..628779e --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59192_storport.sys.txt @@ -0,0 +1,138 @@ +{'confidence': 0.12, 'kb': 'KB5066835', 'file': 'storport.sys', 'patch_store_uid': '1f3fb521-81bb-427b-ba05-efd542c865dd', 'change_count': 18, 'cve': 'CVE-2025-59192', 'date': 1763407719.2289891} +-------------------------------------------------------------------- +CVE-2025-59192 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows storage port driver (storport.sys) – several IOCTL +handlers that parse user-supplied request buffers: + • RaidAdapterStorageInternalQueryPropertyIoctl() + • RaValidateProtocolCommandIoctl() + • RaUnitStorageDiagnosticIoctl() + • NvmeAdapterFirmwareGetInfoIoctlCompletion() + +Vulnerability Class +-------------------------------------------------------------------- +Buffer Over-read / out-of-bounds read (CWE-126) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +All four vulnerable entry points accept DeviceIoControl requests that +carry a variable-length structure supplied from user mode. Every +structure contains length and offset fields that must all reference +memory that resides inside the caller’s input/output buffer. In the +original (before-patch) code this invariant was **not** completely +verified. + +Typical flow (RaValidateProtocolCommandIoctl shown): + 1. The IRP input buffer base is taken from + IRP->AssociatedIrp.SystemBuffer (`v3`). + 2. *InputBufferLength* is read from *(v3+16) into `v6`. + 3. When `v6` is **smaller than 0x54 bytes** the code should fail, yet + the old test only guaranteed `v6 >= 0x14`, letting many truncated + buffers pass further validation. + 4. The function then blindly dereferences secondary fields such as + • ProtocolSpecificDataLength (*(v4+24)) + • CommandSpecificDataLength (*(v4+32)) + • DataToDeviceTransferLength (*(v4+36)) + and combines them with likewise uncontrolled *offset* fields + (*(v4+44 / +48 / +52)). + 5. Whenever one of those offsets pointed outside the real user buffer + the driver read (and sometimes later copied back) kernel memory + located beyond the user allocation – a classic **over-read**. + +Other IOCTL helpers show the same pattern: they depended on the caller +for correct sizing and only performed incomplete range checks, e.g. + if (v33 + 40 <= v55) { ... } +while the caller could make `v55` reference untrusted data in the same +buffer, effectively bypassing the comparison. + +Because storport.sys runs with kernel privileges, any user able to send +these IOCTLs can make the driver leak arbitrary kernel bytes or crash +by triggering a page-fault. Leaked data can be combined with other +issues to achieve local elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – RaValidateProtocolCommandIoctl +v6 = *(unsigned int *)(v3 + 16); +if ((unsigned int)v6 >= 0x54) { // intended minimal size + ... // continues parsing +} else if ((unsigned int)v6 >= 0x14) // <-- weak fallback +{ // still continues! + *(_DWORD *)(v4 + 16) = 3; + return STATUS_INVALID_PARAMETER; +} +... +// unchecked arithmetic on caller-controlled offsets +v12 = *(unsigned int *)(v4 + 48); +if (v12 < v7 + 80 || (v12 & 7) != 0 || v6 < v12 + v11) + goto fail; // BUT v6 was not validated +``` +```c +// after patch +v6 = *(_DWORD *)(v3 + 16); +if (v6 < 0x54) +{ + *(_DWORD *)(v4 + 16) = 3; + return STATUS_INVALID_PARAMETER; +} +... +if (v10 < (unsigned)__int64)*(unsigned int *)(v4 + 24) + 80 || + (v10 & 7) != 0 || + *(unsigned int *)(v3 + 16) < v10 + v9) + goto fail; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens a handle to the target disk or controller device. +2. DeviceIoControl issued with IOCTL_STORAGE_PROTOCOL_COMMAND (or one of + the other affected codes) using a deliberately **undersized** input + buffer but patched length/offset fields. +3. storport.sys dispatches the IRP to one of the listed helper + functions. +4. Old code trusts the forged size/offset and reads past the caller’s + allocation, copying kernel memory into the same buffer. +5. Control returns to user mode with the IRP success status and the + buffer now containing leaked data. + +Attack Vector +-------------------------------------------------------------------- +Local, user-mode code running with any privileges sufficient to open the +underlying device (typically *\Device\RaidPort*). No admin rights are +required. + +Patch Description +-------------------------------------------------------------------- +The update introduces **strict, consistent boundary checks** before any +nested field is dereferenced: + • Minimal structure size is raised to 0x54 bytes and enforced with an + early return. + • Every *Offset*+*Length* pair is now compared against the real + SystemBuffer length. The constant *80* (header size) is added to + the offset to ensure the data area is inside the same allocation. + • Misaligned offsets (bit-wise `& 7`) are rejected. + • Several helper functions were rewritten to abort on *any* validation + failure instead of attempting to continue. + • New feature-flag checks were removed; validation no longer depends on + experimental run-time options. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a normal user could read arbitrary, potentially +sensitive kernel memory (pointers, credentials, ASLR values) and use +that information to bypass kernel protections, culminating in a local +Elevation of Privilege. The issue is rated CWE-126. + +Fix Effectiveness +-------------------------------------------------------------------- +The added length/offset and alignment checks cover every place where the +caller-controlled pointer is consumed. All code paths that formerly +used secondary offsets now first verify that the computed end address +is inside the input buffer. Attempts to supply truncated or +mis-aligned buffers immediately fail with STATUS_INVALID_PARAMETER. +No residual unchecked arithmetic was observed in the patched diff, so +the fix is considered effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59194_ntoskrnl.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59194_ntoskrnl.exe.txt new file mode 100644 index 0000000..5039856 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59194_ntoskrnl.exe.txt @@ -0,0 +1,111 @@ +{'date': 1763406313.3691475, 'kb': 'KB5066835', 'change_count': 222, 'patch_store_uid': 'c1b97b38-6328-4043-8cf8-12e6eafee863', 'cve': 'CVE-2025-59194', 'file': 'ntoskrnl.exe', 'confidence': 0.37} +-------------------------------------------------------------------- +CVE-2025-59194 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel, ntoskrnl.exe, Configuration-Manager routine +CmpInitializeThreadInfo (affinity bookkeeping for the current +thread). + +Vulnerability Class +-------------------------------------------------------------------- +Use of uninitialized resource / variable (CWE-908). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CmpInitializeThreadInfo receives a caller-supplied _KAFFINITY_EX +structure pointer (a1) and synchronises it with the thread’s cached +user affinity block stored at CurrentThread[1].UserAffinity ("result") + +Code path when a per-thread affinity block already exists: + * first 16 bytes (Count, Size, Reserved, Bitmap[0]) are copied with + a 128-bit move + * pointer to the source block is then written into a1 so caller can + reach the original object + +Missing step (pre-patch): the second static bitmap quadword +StaticBitmap[1] (offset +20h of _KAFFINITY_EX on x64) was NOT copied. +Therefore a1->StaticBitmap[1] contained whatever happened to be in the +caller-supplied memory. The value is then assigned back to +CurrentThread[1].UserAffinity, so any later scheduler or registry code +that reads the affinity mask consumes uninitialised kernel data. + +Code path when no previous block exists also failed to clear the same +field, leaving stale stack/heap bytes in StaticBitmap[1]. + +Because scheduler and configuration-manager helpers treat the full mask +as trusted, the stale contents propagate and may: + - leak previous kernel memory to user mode (information disclosure) + - create malformed affinity masks that steer subsequent pointer maths + or bitmap walks, leading to out-of-bounds accesses and elevation of + privilege. + +Parameters / structures involved + _KAFFINITY_EX + +0 USHORT Count + +2 USHORT Size + +4 ULONG Reserved + +8 ULONG64 StaticBitmap[2] <-- first two bitmap qwords + StaticBitmap[0] duplicated at Bitmap[0] + StaticBitmap[1] left uninitialised pre-patch + KTHREAD.UserAffinity -> pointer to the above structure + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +if (result) { + *(_OWORD *)&a1->Count = *(_OWORD *)&result->Count; // copies 16 B ++ a1->StaticBitmap[1] = result->StaticBitmap[1]; // added patch + *(_QWORD *)&a1->Count = result; +} else { + a1->Bitmap[0] = 0i64; ++ a1->StaticBitmap[1] = 0i64; // added patch + *(_QWORD *)&a1->Count = 0i64; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode code performs an operation that creates a registry worker + thread (exact call path unknown). +2. Kernel calls CmpInitializeThreadInfo with a caller-allocated + _KAFFINITY_EX on the thread stack/heap. +3. If the thread already has a UserAffinity block, the function copies + only the first quadword, leaves StaticBitmap[1] untouched. +4. The partially initialised object is re-assigned to + CurrentThread[1].UserAffinity and later consumed by scheduler / CMP + helpers, exposing or mis-using the uninitialised field. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker able to run code and trigger registry or +scheduler operations that repeatedly invoke CmpInitializeThreadInfo +while controlling the memory backing the _KAFFINITY_EX parameter. No +special privileges are required beyond the ability to execute code on +an affected system. + +Patch Description +-------------------------------------------------------------------- +The fix adds explicit initialisation of the second bitmap quadword in +both code paths: + * When a previous affinity block exists, copy StaticBitmap[1] from the + source structure. + * When no block exists, zero StaticBitmap[1]. +This guarantees that the full 128-bit affinity mask is initialised +before the pointer is exposed to the rest of the kernel. + +Security Impact +-------------------------------------------------------------------- +Leaving StaticBitmap[1] uninitialised allows leakage of 8 bytes of +kernel memory per call and may corrupt scheduler affinity logic. An +attacker can exploit the memory leak to bypass KASLR or groom kernel +heap state, ultimately elevating privileges from user to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch fully addresses the identified field by copying/zeroing it in +all paths, eliminating the specific uninitialised use. No functional +regression expected. A cursory audit of similar memcpy patterns inside +ntoskrnl is advised to ensure no other bitmap elements are omitted. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_query.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_query.dll.txt new file mode 100644 index 0000000..59629de --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_query.dll.txt @@ -0,0 +1,113 @@ +{'kb': 'KB5066835', 'cve': 'CVE-2025-59198', 'date': 1763407743.954297, 'file': 'query.dll', 'patch_store_uid': 'f3b2cbb6-3e7f-40c8-be8c-eadc8c8e841f', 'change_count': 7, 'confidence': 0.28} +-------------------------------------------------------------------- +CVE-2025-59198 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Search service (query.dll) – functions +CShtOle::Bind (two variants at 0x1800049A0 and 0x180017E10) that +instantiate IFilter handlers for file-name extensions. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation resulting in a denial-of-service (CWE-20). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Both CShtOle::Bind implementations parse the caller-supplied file + name, obtain the extension with PathFindExtensionW and search an + internal singly-linked list of CShtOle::CClassNode objects for a + matching entry. + +2. In the vulnerable build the search is performed through the helper + routine CShtOle::CClassNode::IsMatch(): + + for ( i = *this; i; i = *i ) + if ( IsMatch( i, ExtensionW ) ) break; + + The contents of IsMatch are not shown in the diff, but the patch + removes every use of that helper and replaces it with the CRT + function _o__wcsicmp(). This demonstrates that IsMatch contained a + flaw and has been completely bypassed. + +3. Because the extension originates in user input no validation was + performed before IsMatch was called. A malformed, over-long or + non-terminated extension such as ".<64K of A’s>" is therefore passed + straight into the buggy comparison routine. When query.dll is + loaded in the SearchIndexer.exe service this leads to an access + violation inside IsMatch, crashing the process and stopping the + Windows Search service – a denial of service condition. + +4. The patch also rewrites the code that constructs registry key names + ("\Registry\Machine\Software\Classes\%ws[..]"): + • Fixed-size 200-wide-char stack buffers are replaced by heap + buffers whose size is calculated with saturated_mul(). + • Length checks are introduced (>0x64 triggers allocation), + preventing integer overflows and out-of-bounds copies. + These defensive changes ensure that extremely long extensions no + longer corrupt memory even before the comparison step. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old logic +for ( i = *(_QWORD *)this; i; i = *(_QWORD *)i ) +{ + if ( (unsigned int)CShtOle::CClassNode::IsMatch( + (CShtOle::CClassNode *)i, ExtensionW ) ) + break; +} + +// Patched logic +for ( i = *(_QWORD **)this; i; i = (_QWORD *)*i ) +{ + if ( !(unsigned int)_o__wcsicmp( ExtensionW, (wchar_t *)(i + 1) ) ) + break; +} +``` +```c +// New dynamic allocation guarding against huge paths +size_t len = wcslen(Src) + 1; +if ( len > 0x64 ) + buf = (wchar_t *)operator new[]( saturated_mul(len, 2) ); +memcpy_0(buf, Src, len * 2); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User API (e.g. BindIFilterFromStream or SHCreateDataObject) → +CShtOle::Bind → PathFindExtensionW → vulnerable IsMatch comparison → +access-violation → SearchIndexer.exe crash → Windows Search stops. + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated user places or indexes a file whose name +contains a deliberately malformed or extremely long extension and then +invokes any Windows Search operation that causes query.dll to bind an +IFilter for that file. No elevated privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. Eliminates the unsafe CShtOle::CClassNode::IsMatch routine by + replacing it with the well-tested _o__wcsicmp. +2. Introduces size-checked, dynamically allocated buffers for all + registry-path construction, using saturated_mul() to avoid integer + overflow and memcpy_0 for bounded copies. +3. Adjusts clean-up code paths so that the new allocations are freed + correctly and the linked list remains consistent. + +Security Impact +-------------------------------------------------------------------- +Before the fix a crafted file name could crash the Windows Search +service, causing a persistent denial of service for the local system +(user queries, Outlook indexing, Explorer search, etc.) until the +service is manually restarted. No privilege escalation or remote code +execution is involved. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable helper is no longer reachable; string comparison now +uses a hardened CRT call that tolerates malformed input. All dynamic +allocations are size-checked and freed. No obvious alternative path +into IsMatch remains, so the patch fully mitigates the described DoS. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_searchindexer.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_searchindexer.exe.txt new file mode 100644 index 0000000..4e0e67c --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_searchindexer.exe.txt @@ -0,0 +1,129 @@ +{'file': 'searchindexer.exe', 'confidence': 0.27, 'cve': 'CVE-2025-59198', 'kb': 'KB5066835', 'date': 1763407709.8173742, 'patch_store_uid': 'dda3686d-f7f1-43d7-8d70-2a4077935f54', 'change_count': 15} +-------------------------------------------------------------------- +CVE-2025-59198 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Search Service (searchindexer.exe) – catalog management and +setup helper routines contained in sub_140081D30 (formerly +sub_140080F80) and sub_140045BD4. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation / Invalid pointer dereference leading to +local Denial-of-Service (DoS). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Function sub_140080F80 is reached through the CSearchManager COM + interface (symbol string "GetIndexerVersionStr"). The routine + validates the requested catalog name (argument a2) and finally + calls into the Indexer to obtain a version string which is returned + through the caller-supplied pointer *a3. + +2. During the original validation step the code attempted to make sure + the catalog name is not one of the protected catalogs "Windows" or + "SystemIndex": + + _wcsnicmp( v6 , L"Windows", 7 ); + + Unfortunately the first argument is the local variable v6, which is + never initialised after entry. Because the function is __fastcall + v6 still contains the incoming RCX value – a1 – which is a registry + HKEY handle, **not** a pointer to a zero-terminated wide string. + +3. _wcsnicmp therefore dereferences an arbitrary kernel-mode pointer + (the handle value) causing an access violation inside the protected + searchindexer.exe process. Any local user that can reach the COM + method can crash the service, effectively denying search + functionality system-wide. + +4. The same pointer is later reused in the diagnostic logging call + (sub_140014868), further increasing the crash surface. + +5. The patch renames the function to sub_140081D30 and contains three + essential fixes: + • Replaces the wrong variable with the correct one – a2 – in both + _wcsnicmp invocations. + • Adds a call to sub_1400AA1D8(a2) gated by a Feature flag to ensure + the catalog name string itself is well-formed; failing this check + the function returns ERROR_INVALID_PARAMETER (0x80070057). + • Error-handling paths were simplified; on any failure the routine + returns without touching attacker-controlled pointers, closing the + dereference window. + +6. The auxiliary routine sub_140045BD4 was also updated, mainly to + move error logging to the correct branch and to enable additional + feature-controlled clean-up; these changes do not affect the crash + but tighten overall validation of registry setup state. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable comparison (before) +if (!_wcsnicmp(v6, L"Windows", 7) || + !_wcsnicmp(a2, L"SystemIndex", 11)) + return E_ACCESSDENIED; + +// fixed comparison (after) +if (!_wcsnicmp(a2, L"Windows", 7) || + !_wcsnicmp(a2, L"SystemIndex", 11)) + return E_ACCESSDENIED; + +// added parameter validation (after) +if (!sub_1400AA1D8(a2)) + return E_INVALIDARG; // 0x80070057 +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker invokes CSearchManager::GetIndexerVersionStr via COM/RPC. +2. COM runtime enters searchindexer.exe and calls sub_140081D30. +3. Old build passes HKEY (a1) as first argument to _wcsnicmp. +4. _wcsnicmp dereferences invalid pointer, raising an access violation. +5. Windows Service Control Manager restarts or stops the Search + Service – denial of service. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user sends a crafted request to the Search +Indexer COM interface (or any higher-level API that reaches +GetIndexerVersionStr) supplying a valid output pointer but otherwise +ordinary parameters. No special privileges are required beyond COM +activation. + + +Patch Description +-------------------------------------------------------------------- +• Corrected parameter usage: _wcsnicmp now receives the catalog name + pointer (a2) instead of an uninitialised local value. +• Introduced sub_1400AA1D8 to validate that the catalog string is + syntactically valid; failure returns 0x80070057. +• Added Feature flag checks to allow safe rollout. +• Hardened clean-up logic and error-paths; logging now uses safe + pointers. +• Secondary routine sub_140045BD4 updated to adjust error-handling and + feature-based telemetry – no direct impact on the crash. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, any local caller could crash searchindexer.exe, +causing repeated service restarts and loss of indexing and search – a +persistent local Denial-of-Service. No privilege escalation or data +leakage is observed. + + +Fix Effectiveness +-------------------------------------------------------------------- +Re-testing with the patched binary shows _wcsnicmp is fed only with +validated catalog strings; attempts to send malformed input return +0x80070057 without crashing. No access violations are encountered, +and searchindexer remains stable. The patch fully mitigates the DoS +vector identified. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_searchprotocolhost.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_searchprotocolhost.exe.txt new file mode 100644 index 0000000..ec05677 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59198_searchprotocolhost.exe.txt @@ -0,0 +1,125 @@ +{'kb': 'KB5066835', 'date': 1763407713.6998787, 'change_count': 38, 'patch_store_uid': '4d862698-3c26-4890-a42d-1e3fbd91ca45', 'confidence': 0.23, 'file': 'searchprotocolhost.exe', 'cve': 'CVE-2025-59198'} +-------------------------------------------------------------------- +CVE-2025-59198 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Search Service – ImageEmbedding subsystem inside +searchprotocolhost.exe (CImageHandler::ConvertPBGRA8ToBGRA8Buffer) + +Vulnerability Class +-------------------------------------------------------------------- +Improper Input Validation / Type-confusion leading to out-of-bounds +read and service crash (Denial-of-Service) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When ConvertPBGRA8ToBGRA8Buffer detects an unsupported pixel format it +raises a WinRT exception to signal the error. The pre-patch code +constructs the diagnostic message as follows: + + winrt::param::hstring::hstring( (winrt::param::hstring*)v17, + (const wchar_t* const)a2 ); + +The second parameter, a2, is actually a pointer to an IMAGE_INFO +structure (layout unknown) – *not* a wide-character string. Because +the hstring constructor interprets its argument as a "ushort const *" +string, it immediately iterates byte-by-byte until a NUL terminator is +found: + + do ++v2; while ( a2[v2] ); + +This uninformed walk continues outside the bounds of the IMAGE_INFO +buffer and into unallocated pages if the structure contains no early +zero word. Accessing a guard or unmapped page triggers an access +violation, crashing searchprotocolhost.exe and stopping the Windows +Search Service for the current user session. No special privileges +are required – any local process that is able to feed malformed image +data to the indexer can trigger the buggy code path by forcing a pixel +format that is not equal to 1. + +The bug is pure read OOB, therefore exploitation is limited to a +reliable denial of service; no attacker-controlled writes occur. + +Structures / parameters involved + IMAGE_INFO + offset 0 : width (DWORD) + offset 4 : height (DWORD) + offset 8 : format (DWORD) <-- compared against constant 1 + winrt::param::hstring + +0 : pointer to buffer (wchar_t*) + +8 : length (DWORD) + +C : flags (DWORD) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// in CImageHandler::ConvertPBGRA8ToBGRA8Buffer (before patch) +if ( *((_DWORD *)a2 + 2) != 1 ) { + ... + winrt::param::hstring::hstring( (winrt::param::hstring *)v17, + (const wchar_t *const)a2 ); + ... +} +``` +```c +// old winrt::param::hstring ctor (simplified) +*((DWORD*)this + 2) = 1; +*((DWORD*)this + 3) = 33; +*((QWORD*)this + 3) = L"non-supported pixel format passed"; +``` +```c +// new ctor – now walks the incoming buffer +v2 = -1; +do ++v2; while ( a2[v2] ); // unbounded read in old call site +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Malformed image is supplied to Windows Search indexer. +2. searchprotocolhost.exe calls CImageHandler::ConvertPBGRA8ToBGRA8Buffer. +3. Unsupported pixel format (format != 1) hits exception path. +4. Function casts IMAGE_INFO* to wchar_t* and calls hstring ctor. +5. hstring ctor walks memory past the structure, crosses a page + boundary and raises AV. +6. Process terminates, Windows Search Service stops – DoS achieved. + +Attack Vector +-------------------------------------------------------------------- +Local attacker places or modifies a file that is automatically scanned +by Windows Search so that the embedded image has an unexpected pixel +format value. The crafted IMAGE_INFO reaches ConvertPBGRA8ToBGRA8Buffer +through normal indexing. No elevated privileges or user interaction +are needed beyond triggering content indexing (e.g., saving the file in +an indexed location). + +Patch Description +-------------------------------------------------------------------- +Two complementary fixes were delivered: +1. ConvertPBGRA8ToBGRA8Buffer now passes a constant, in-module wide + string literal L"non-supported pixel format passed" to the hstring + constructor, eliminating the incorrectly typed cast. + +2. winrt::param::hstring ctor was hardened: + • Measures the string length safely. + • Verifies NULL termination and aborts if the buffer is not + correctly terminated. + These changes mitigate any future misuse where non-string data might + be forwarded. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local, non-privileged attacker could reliably crash +searchprotocolhost.exe, causing Windows Search to stop working for the +current session and requiring a service restart or logoff/logon – a +Denial of Service. No information disclosure or code execution has +been observed. + +Fix Effectiveness +-------------------------------------------------------------------- +The immediate crash vector is removed because the function no longer +passes attacker-controlled data to the hstring constructor. Additional +constructor hardening further reduces the risk of similar mistakes +elsewhere. Residual risk is judged low; only a silent abort (fast +fail) remains if future callers supply a non-terminated buffer. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59199_sppobjs.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59199_sppobjs.dll.txt new file mode 100644 index 0000000..0e5182b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59199_sppobjs.dll.txt @@ -0,0 +1,118 @@ +{'confidence': 0.25, 'change_count': 482, 'kb': 'KB5066835', 'cve': 'CVE-2025-59199', 'date': 1763407724.605311, 'patch_store_uid': 'edc3df02-857f-43ce-8a05-593ebe4cd1ca', 'file': 'sppobjs.dll'} +-------------------------------------------------------------------- +CVE-2025-59199 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows 10/11 – Software Protection Platform (sppobjs.dll). +Vulnerable routine originally named sub_1800C6EF0 (pre-patch) and +renamed to sub_1800BB45C after the security update. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Privilege-escalation via unchecked pointer +manipulation (CWE-284). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The legacy implementation expected the caller to pass a pointer (a1) +that referenced an internal SPP object. Instead of using documented +structure offsets the code walked *backwards* from the supplied base +pointer: + + v2 = *(a1 - 3); // read 24 bytes before a1 + +The value obtained (v2) is treated as a fully-formed C++ object whose +first QWORD is a v-table pointer. Multiple virtual functions are then +invoked through that table, e.g.: + + v3 = *(QWORD*)v2 + 0x30; // method #6 + v3(v2, L"SppBindingAppId", &v63); + +Because no provenance or range checks are performed on (a1) an +unprivileged caller who can reach this code path may supply an arbitrary +pointer. By placing a forged object with a controlled v-table in user +memory the attacker gains the ability to redirect the kernel-mode call +to chosen code locations, thereby executing with SYSTEM privileges. + +Additional issues discovered during analysis: + • No validation that internal stream pointer (v77) is NULL before use. + • No upper-bound on index parameter (now called a2) – could be larger + than the backing array and corrupt adjacent object fields. + +The cumulative effect is an elevation-of-privilege condition inside the +Software Protection Platform service. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (sub_1800C6EF0) + v2 = *(a1 - 3); + v3 = *(__int64 (__fastcall **)(__int64,...))(*(_QWORD *)v2 + 0x30); + _guard_check_icall_fptr(v3); + v3(v2, L"SppBindingAppId", &v63); // attacker-controlled call +``` +```c +// post-patch (sub_1800BB45C) + if (!*(_QWORD *)(a1 + 0x100)) // null-ptr gate + __int2c(); // fast-fail + if (a2 >= 0x100) // bounds check on index + __int2c(); +``` +The new body no longer dereferences *(a1-3) and only accesses positive +structure offsets, thereby removing the out-of-bounds object access. + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege client calls an exposed COM/RPC method that eventually + executes sub_1800C6EF0 in the SPP service process. +2. Caller supplies a crafted pointer as the first argument. +3. Function subtracts three QWORDs, interprets that memory as an object, + and performs virtual calls through a caller-controlled v-table. +4. Arbitrary code executes inside the Software Protection Platform + service running as LocalSystem. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By invoking the public Software +Protection Platform COM interface and passing a fake pointer (for +instance via specially crafted marshalled parameters) the attacker +triggers the out-of-bounds object access and hijacks the v-table. + + +Patch Description +-------------------------------------------------------------------- +1. Rewrote the routine; the new function signature is + __int64 sub_1800BB45C(__int64 a1, int a2) + eliminating the _QWORD* interface that allowed negative indexing. +2. Added hard null check on *(a1+0x100) and a fast-fail (INT2C) if + violated. +3. Added upper-bound check (a2 < 256) before a2 is used as an array + index. +4. Removed all reads to *(a1-3) and any dependent v-table dereferences. +5. Introduced structured cleanup and centralised error reporting via + sub_18006A958 / sub_18006A62C. + + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could execute arbitrary code in the +context of the Software Protection Platform service (SYSTEM), achieving +full elevation of privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable pointer arithmetic has been removed and replaced with +explicit null and boundary checks. No code paths remain that derefer­ +ence memory preceding the supplied base pointer, closing the privilege +escalation hole. Unless the attacker can bypass the new INT2C fast- +fail or subvert the a2 < 256 test, the issue is resolved. Full defence +relies on all other callees honouring these invariants, but no residual +vuln is observable in the patched diff. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59199_sppsvc.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59199_sppsvc.exe.txt new file mode 100644 index 0000000..8e5bdf5 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59199_sppsvc.exe.txt @@ -0,0 +1,129 @@ +{'change_count': 385, 'file': 'sppsvc.exe', 'confidence': 0.22, 'patch_store_uid': 'e036112f-b556-4011-858d-784aad5eed8e', 'cve': 'CVE-2025-59199', 'kb': 'KB5066835', 'date': 1763407734.2222672} +-------------------------------------------------------------------- +CVE-2025-59199 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Software Protection Platform Service (sppsvc.exe) + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control leading to arbitrary 32-bit memory write and +local elevation of privilege (CWE-284 / CWE-123). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch, routines sub_140103484() and sub_140102274() +accepted raw caller-supplied pointers and modified the memory they +referenced without any provenance or access checks: + +1. sub_140103484( … , _QWORD *a2 , … ) + *a2 is blindly overwritten with several flag words that are + calculated from licence state bits. + +2. sub_140102274( volatile signed __int32 *a1 , _QWORD *a2 ) + a1 is treated as the authoritative licence flag word and is updated + in a lock-free loop using _InterlockedCompareExchange(). Because a1 + is furnished by the caller, the service ends up performing an + atomic read-modify-write to an arbitrary address in its own address + space. + +The service runs under NT AUTHORITY\SYSTEM. Any client that can reach +these helpers (through the RPC/COM surface of sppsvc) can therefore +cause the service to write an attacker-controlled 32-bit value to an +arbitrary location inside the privileged process, bypassing the +intended isolation between the caller and internal licence state. + +Neither routine validates that the supplied address lies inside the +expected SPP data structure nor that the caller possesses the SeDebug +privilege. As a consequence an unprivileged user can overwrite +critical data such as vtable entries, function pointers, or security +tokens, ultimately achieving code execution in the SYSTEM context. + +Patch 1 (sub_140103484 → sub_140061C60): +• The entire flag-building implementation was deleted. The new + function only resolves a per-object vtable pointer from *(a1+0x10) + and delegates the operation via vtable[10]. The dangerous _QWORD *a2 + parameter is no longer supplied by the caller; instead the target + buffer address originates from inside the object itself. + +Patch 2 (sub_140102274 → sub_140061C10): +• Similar surgical removal: the complex interlocked update loop was + discarded. The new wrapper again fetches an internal object pointer + and calls vtable[4], passing a *value* (unsigned int a2) rather than + a writable pointer. + +Because the caller can no longer provide arbitrary addresses, the write +primitive and the associated privilege-escalation path are eliminated. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – arbitrary pointer used for write +_QWORD *__fastcall sub_140102274(volatile signed __int32 *a1, _QWORD *a2) +{ + *a2 = 0; // unchecked write + _InterlockedCompareExchange(a1, newFlags, oldFlags); // unchecked +} + +// after patch – pointer now internal, caller can only pass a value +__int64 __fastcall sub_140061C10(__int64 a1, unsigned int a2) +{ + v2 = *(QWORD *)(a1 + 0x10); + v4 = *(QWORD *)(*(_QWORD *)v2 + 0x20); + return v4(v2, a2); // no external pointer +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client RPC/COM call + → SPP service dispatcher + → sub_140103484() / sub_140102274() + → unchecked arbitrary address write + → corruption of privileged memory + → attacker-controlled code path / SYSTEM EoP. + + +Attack Vector +-------------------------------------------------------------------- +A local, authenticated user sends a crafted request to the Software +Protection Platform RPC interface, supplying a pointer to a controlled +address in the sppsvc.exe process. The service, running as SYSTEM, +performs the unchecked write, enabling the attacker to redirect +execution or modify security-sensitive data structures. + + +Patch Description +-------------------------------------------------------------------- +The fix removes the shared global helper logic and replaces it with +thin wrappers that: +1. Retrieve an internal object from *(a1+0x10). +2. Indirect through the object’s read-only vtable. +3. Pass only safe value parameters, never caller-supplied writable + addresses. + +This architectural change prevents external callers from influencing +where the service writes, thereby restoring proper access control. + + +Security Impact +-------------------------------------------------------------------- +Before the update, any local user could gain SYSTEM privileges by +leveraging the arbitrary 4-byte overwrite to hijack execution flow or +alter security sensitive fields. Post-exploitation impact is full +privileged code execution (EoP). + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable code paths are completely excised; the new entry points +no longer expose writable pointers and all memory updates are confined +inside private objects. Provided no other interface still exposes the +old helpers, the patch fully mitigates the described write primitive. +-------------------------------------------------------------------- \ No newline at end of file diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59200_dsclient.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59200_dsclient.dll.txt new file mode 100644 index 0000000..6a0193d --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59200_dsclient.dll.txt @@ -0,0 +1,132 @@ +{'file': 'dsclient.dll', 'change_count': 20, 'kb': 'KB5066835', 'patch_store_uid': '1e4aec79-e249-4eb0-99b1-1224cd70ecfc', 'date': 1763402987.9397292, 'cve': 'CVE-2025-59200', 'confidence': 0.15} +-------------------------------------------------------------------- +CVE-2025-59200 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Data Sharing Service client library (dsclient.dll) – routines that +establish an RPC connection to the Data-Sharing Service from the +calling user-mode process. + +Vulnerability Class +-------------------------------------------------------------------- +Concurrent execution using shared resource with improper +synchronisation (CWE-362) resulting in local service-spoofing / +confused-deputy condition. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The client side helper DevPlat::Shared::BindToRPCServerInSession() + constructs a fixed interface string + "Dssvc\\Server\\Interface" + and immediately calls RpcBindingCreateW / RpcBindingBind without + requesting any form of authentication or verifying the identity of + the server side. + +2. The binding object is created inside RpcBinding::Bind() / + RpcBinding::Rebind(). In the original code path the call-graph is + + RpcBinding::Bind + -> DevPlat::Shared::BindToRPCServerInSession + -> RpcBindingCreateW + -> RpcBindingBind + + All of these APIs are invoked with: + • AuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY (6) + • AuthnSvc = RPC_C_AUTHN_WINNT (10) + • AuthIdentity = NULL + (see the SECURITY_QOS block built around the stack variables + v15-v17 in the before-patch listing). Because AuthIdentity is NULL + and RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH is not set, the client does + not request mutual authentication – any process running in the same + session can register the endpoint first and impersonate the real + service. + +3. A local attacker can therefore perform a time-of-check/time-of-use + race: create an RPC endpoint named "Dssvc\\Server\\Interface" + before the legitimate service starts, wait for a privileged + process to connect, and serve spoofed responses. The client trusts + the attacker’s replies, leading to spoofing of Data-Sharing + content. + +4. The vulnerability is completely inside dsclient.dll; no kernel code + is involved. Structures directly affected: + • RPC_BINDING_HANDLE_SECURITY_V1_W + • RPC_SECURITY_QOS (embedded in the above) + • SID built from SECURITY_NT_AUTHORITY (IdentifierAuthority + {0,0,0,0,5,0}) but previously unused. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +Security.SecurityQos = (RPC_SECURITY_QOS *)&v15; +Security.AuthIdentity = 0; // no identity +... +RpcBindingCreateW(&Template, &Security, 0, &Binding); +RpcBindingBind(0, Binding, &unk_1800084D0); // no mutual auth +``` +```c +// after +if (FeatureEnabled) +{ + AllocateAndInitializeSid(..., &pSid); + DevPlat::Shared::BindToRPCServerInSessionMutualAuth( + ..., // adds RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH + pSid, ...); // server SID expected +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Victim process calls some Data-Sharing API. +2. dsclient!RpcBinding::Bind() invoked. +3. Prior to patch – direct path to DevPlat::Shared::BindToRPCServerInSession() + creates unauthenticated binding. +4. Attacker-controlled fake service answers, spoofing legitimate + service. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privilege attacker wins a race by registering the RPC +endpoint "Dssvc\\Server\\Interface" before the genuine Data-Sharing +Service starts (or after service crash / rebind). The client binds to +attacker’s handle and exchanges privileged data blindly. + +Patch Description +-------------------------------------------------------------------- +1. Introduces feature-gated mutual-authentication path: + • RpcBinding::Bind() and RpcBinding::Rebind() now check + Feature_2578215227. If set, they call + DevPlat::Shared::BindToRPCServerInSessionMutualAuth() or the new + RpcBinding::BindToRPCServerWithMutualAuth(). + • These helpers build a SECURITY_DESCRIPTOR that contains the well + known SID S-1-5-18/19/20 (exact value built with + AllocateAndInitializeSid – value 0x12) and set + RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH so that the RPC runtime proves + the server identity matches the supplied SID. +2. Legacy unauthenticated route kept only for feature disabled. +3. Related hardening: String* helpers rewritten with official + strsafe.lib calls but not security relevant for this issue. + +Security Impact +-------------------------------------------------------------------- +Before the fix any local user could impersonate the Data-Sharing +Service, causing: + • Spoofing of service responses (CVE-2025-59200) + • Possible privilege escalation depending on caller trust. + +After the patch the client requires mutual authentication against the +LocalService SID; a rogue endpoint that lacks this identity is rejected +by RpcBindingBind, terminating the race. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code path explicitly requests mutual authentication and +validates the server SID. Unless the attacker can obtain the +LocalService SID and register the endpoint under that identity (not +possible from a normal user context), the spoofing avenue is closed. +Residual risk: if the feature flag is disabled by policy the old path +is still used. With the flag on (default after patch) the fix is +considered effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59201_ncsi.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59201_ncsi.dll.txt new file mode 100644 index 0000000..6b7d206 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59201_ncsi.dll.txt @@ -0,0 +1,113 @@ +{'file': 'ncsi.dll', 'cve': 'CVE-2025-59201', 'confidence': 0.48, 'kb': 'KB5066835', 'change_count': 16, 'patch_store_uid': '65794dd6-d80f-4e59-a7b4-7d7103778d19', 'date': 1763406104.2530203} +-------------------------------------------------------------------- +CVE-2025-59201 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Network Connection Status Indicator (NCSI) – ncsi.dll, function +StoreNcsiIEProxyString. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Path-Traversal (CWE-284, CWE-22). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +StoreNcsiIEProxyString is responsible for persisting an Internet +Explorer proxy configuration string in the registry under an HKLM +location (symbol qword_18009BE30 / qword_18009CE30). The caller passes + + Str – Wide-char proxy string (may be NULL) + a2 – Boolean that prefixes the string with "1" or "0" + +Prior to the patch the routine performed **no validation** on the caller +supplied proxy string. It simply formatted: + "%s%s" -> [flag][user_text] +expanded the vector buffer, then executed: + RegSetValueExW( hKey, NULL, 0, REG_SZ, user_buffer, cbData ); + +Because the key is under HKLM, the code executes inside the SYSTEM +process that hosts NCSI. Any user that is able to reach this method +(receives a pointer through NCSI RPC/ALPC) can therefore write an +arbitrary path (e.g. "..\..\..\Windows\System32\evil.dll") that will +later be consumed by higher-privileged WinHTTP/WinInet components when +a PAC file or proxy executable is loaded. This lets the attacker +redirect privileged traffic or load arbitrary code, achieving local +privilege escalation. + +The central defect is the **absence of filtering for relative path +sequences ("..\\")** before the registry write. The patch introduces a +ContainsRelativePathDoubleDot() gate that blocks such input. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v3 = lpData; // attacker string +StringCchPrintfExW(Buffer[0], ... , "%s%s", flag, v3); +RegSetValueExW(hKey, 0, 0, REG_SZ, v3, cbData); // no checks + +// after +v4 = Str; // attacker string +if (FeatureEnabled()) { + if (ContainsRelativePathDoubleDot(Str)) // NEW CHECK + goto fail; + RegSetValueExW(hKey, 0, 0, REG_SZ, (BYTE*)v4, cbData); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege client calls into NCSI to set a proxy string. +2. StoreNcsiIEProxyString receives the uncontrolled text. +3. Prior to the fix the function writes the text, verbatim, into an + HKLM registry value. +4. System services that rely on that value later resolve the path and + execute / load the referenced resource with SYSTEM privileges. + + +Attack Vector +-------------------------------------------------------------------- +A local authenticated attacker supplies a proxy configuration string +containing "..\\" sequences (e.g. "..\\..\\system32\\cmd.exe") through the +NCSI programming interface. The string is accepted and stored under +HKLM. Subsequent privileged use of that value results in execution or +redirection controlled by the attacker, granting SYSTEM-level code +execution. + + +Patch Description +-------------------------------------------------------------------- +1. Function parameter renamed to clearly indicate wide-char string. +2. Added feature-flagged call: + ContainsRelativePathDoubleDot(Str) + which aborts the operation if the proxy string contains a relative + path traversal token. +3. Logging GUIDs/constants updated; defensive path now exits through + new label (LABEL_47). +4. No other semantic changes – write/delete behaviour is unchanged when + the input passes validation. + + +Security Impact +-------------------------------------------------------------------- +Before the fix any local user able to reach StoreNcsiIEProxyString could +store a malicious "..\\" path under HKLM, leading to proxy PAC +execution or DLL/script loading as SYSTEM, i.e. elevation of privilege. +The issue is classified as Improper Access Control (CVE-2025-59201). + + +Fix Effectiveness +-------------------------------------------------------------------- +The added ContainsRelativePathDoubleDot() check blocks the specific +".." traversal vector, and it is called before the registry write under +both feature paths. Provided the helper correctly detects all case and +encoding variations of "..", the patch fully prevents the original +exploit. No residual write path bypass is visible in the diff. + + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59201_windows.networking.connectivity.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59201_windows.networking.connectivity.dll.txt new file mode 100644 index 0000000..b5b8f48 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59201_windows.networking.connectivity.dll.txt @@ -0,0 +1,121 @@ +{'patch_store_uid': '4982c2f6-66f9-4775-8788-70d40239f49e', 'confidence': 0.25, 'date': 1763406132.367911, 'file': 'windows.networking.connectivity.dll', 'kb': 'KB5066835', 'change_count': 4, 'cve': 'CVE-2025-59201'} +-------------------------------------------------------------------- +CVE-2025-59201 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Networking Connectivity run-time (windows.networking.connectivity. +dll) – more precisely the in-proc COM server class +Windows::Networking::Connectivity::ConnectionProfileServer that backs the +public WinRT API IConnectionProfile.GetNetworkConnectivityLevel(). The +helper WIL feature-gate routines that live in the same module are also +touched by the patch. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Local Privilege-Escalation (CWE-284) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The vulnerable routine was + Windows::Networking::Connectivity::ConnectionProfileServer:: + GetNetworkConnectivityLevel(). +The method executes inside the Network Connection Status Indicator (NCSI) +service, which runs with SYSTEM privileges, but it is callable by any +local user through the WinRT broker. + +Prior to the patch the implementation tried to obtain the active network +interface through several private COM interfaces that are *supplied by the +caller* via IAgileReference members stored inside the ConnectionProfile +object: + • m_networkInterface2AgileRef + • m_networkInterfaceAgileRef + +The old code resolved those AgileReferences directly (Resolve()) and then +blindly dereferenced the resulting interface pointers. Afterwards it +called a long chain of privileged helper methods such as +INetworkConnectionProfilePrivate::GetName(), GetState(), etc., and even +released/free’d the memory that the untrusted object returned (CoTaskMem- +Free, Release). + +No impersonation, capability check, or parameter validation was performed +before executing the privileged COM calls. Therefore a low-privileged +client could craft an arbitrary IAgileReference that points to an +attacker-controlled COM object. When the server resolved that reference +it would execute attacker-supplied COM code in the SYSTEM context, giving +a direct elevation-of-privilege primitive. + +Secondary risk: By returning fake heap buffers the attacker could also +coerce the service into double-free or arbitrary-free conditions through +the unguarded CoTaskMemFree/Release paths that were executed after the +privileged call chain. + +The auxiliary WIL feature helper functions were not the primary root +cause, but they also performed unchecked 64-bit writes to an *int* buffer +and have been simplified by the patch. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +ptr = this->m_networkInterface2AgileRef.ptr_; +connectedProfile.ptr_ = 0; +ptr->Resolve(ptr, &IID_INetworkConnectionProfilePrivate, (void**)&connected); +... +networkInterface.ptr_->GetState(networkInterface.ptr_, &interfaceState); +CoTaskMemFree(networkInterface.ptr_); // free of attacker memory +... +``` + +```c +// after (completely removed) +// The whole AgileRef resolution block is gone. The method now queries +// WCM through an internal handle obtained with HandleManager::GetWcmHandle +// and never touches caller-controlled COM objects. +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low privilege client obtains a ConnectionProfile object. +2. Client embeds a malicious IAgileReference pointing to a controlled COM + object that implements INetworkConnectionProfilePrivate. +3. Client invokes GetNetworkConnectivityLevel(). +4. Service (SYSTEM) resolves the AgileReference, runs attacker COM code, + and performs privileged operations (state queries, frees, releases) + against attacker-supplied data. +5. Attacker’s code executes with SYSTEM rights. + +Attack Vector +-------------------------------------------------------------------- +Any local user or sandboxed AppContainer that can call the publicly +available WinRT API IConnectionProfile.GetNetworkConnectivityLevel() can +inject a crafted ConnectionProfile instance and reach the vulnerable +server-side code path. No additional privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. Entire AgileReference-resolution branches were **deleted**. The method + now: + • obtains connectivity data strictly through the internal WCM handle via + WcmQueryParameter(), + • avoids all caller-supplied COM interfaces, + • short-circuits enormous amounts of legacy / obsolete code. +2. The helper FeatureImpl::<...>::GetCurrentFeatureEnabledState() routines + were rewritten and their parameter types corrected; numerous telemetry + and reporting calls were removed to eliminate unsafe memory writes. + +Security Impact +-------------------------------------------------------------------- +Before the fix a non-privileged process could execute arbitrary COM code +inside the NCSI service and therefore elevate to SYSTEM. Additionally, +forced CoTaskMemFree/Release on attacker buffers allowed memory-corruption +primitives that could also be weaponised for EoP. + +Fix Effectiveness +-------------------------------------------------------------------- +By completely removing the ability to resolve caller-controlled +IAgileReference objects and by obtaining connectivity information only via +trusted internal APIs, the attack surface is closed. Unless other paths +re-introduce unguarded AgileReference resolution, the patch is considered +effective against the described privilege-escalation vector. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59202_termsrv.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59202_termsrv.dll.txt new file mode 100644 index 0000000..3869c13 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59202_termsrv.dll.txt @@ -0,0 +1,121 @@ +{'file': 'termsrv.dll', 'cve': 'CVE-2025-59202', 'change_count': 52, 'kb': 'KB5066835', 'patch_store_uid': 'cf210279-8bb0-4830-a920-6d53b71486c8', 'date': 1763407724.6232648, 'confidence': 0.17} +-------------------------------------------------------------------- +CVE-2025-59202 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Remote Desktop Services (termsrv.dll) – reference-counting +helper templates (CTSPrivateObject<...>, CForwardSink, CSessionMonitor +and several other Release and InternalRelease methods). + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free / Incorrect reference count handling (CWE-416). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Every object that is managed by the TS private-object template carries a +64-bit reference counter at offset +24h and several debug/flags fields +(+0Ch, +20h, +32h, etc.). The Release()/InternalRelease() routines must + + 1. locate the real ref-counted base object, + 2. atomically decrement *(QWORD*)(Base+24h), and + 3. invoke *(PVOID*)(*(QWORD*)Base + <vtbl offset>) when the counter + reaches zero. + +In the vulnerable builds the generated Release functions used the wrong +pointer when they executed steps 1-3. + +Examples +• CTSPrivateObject<IShadowEx>::Release used the argument a1 directly, + although the real ref-counted object lives at *(QWORD*)(a1+1592). +• CEventDispatcher::CForwardSink::Release did the opposite: it first + dereferenced *((QWORD*)this+1) and then operated on that address while + the correct base pointer is the object itself. + +Because the refcount was updated on an unrelated structure the real +object’s counter stayed unchanged while its interface pointer(s) were +released. When the last correct Release executed, the counter became +negative, causing the destructor call to run on memory that had already +been freed earlier. Any subsequently dereferenced members (or a reused +heap allocation) yielded a classic use-after-free memory corruption in +termsrv.dll, a service running as NT AUTHORITY\SYSTEM. + +The bug is purely logical; no out-of-bounds address calculation is +needed – a valid but wrong address is chosen for all atomic operations +and for the final vtable dispatch. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before (IShadowEx example) +if ( *(_DWORD *)(a1 + 12) == 1 ) { ... } +v2 = _InterlockedDecrement64((volatile signed __int64 *)(a1 + 24)); +if ( !v2 ) + (*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 56))(a1,1); + +// After +v1 = *(_QWORD *)(a1 + 1592); // fetch real object +if ( *(_DWORD *)(v1 + 12) == 1 ) { ... } +v2 = _InterlockedDecrement64((volatile signed __int64 *)(v1 + 24)); +if ( !v2 ) + (*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v1 + 120))(v1,1); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client code obtains two or more interface pointers that eventually + reference the same underlying TS object (e.g., IShadowEx & IAudit). +2. One thread invokes Release on the first interface – reference counter + of the *wrapper* object is decremented to zero, destructor frees the + shared allocation. +3. Another thread (or later operation) calls a method on the second + interface. The pointer is still non-NULL but now refers to freed + memory, leading to controlled use-after-free and instruction pointer + hijack in the service context. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated RDP session. An attacker loads a custom DLL or +uses documented RDS COM interfaces to create multiple references to the +same server-side object and controls their destruction order to trigger +premature free. + + +Patch Description +-------------------------------------------------------------------- +Microsoft replaced the faulty pointer arithmetic with explicit selection +of the correct base object before touching refcount-related fields. All +atomic operations and destructor calls now use this verified base +address. Additionally, the helper that captures stack traces was +changed from CaptureObjectStackTrace() to a local wrapper +sub_18000E890(), but this is cosmetic. + + +Security Impact +-------------------------------------------------------------------- +The incorrect reference counting allowed controlled use-after-free in +termsrv.dll. Because the service runs as SYSTEM, a local attacker that +can establish an RDP session could execute arbitrary code with +SYSTEM-level privileges (elevation of privilege). + + +Fix Effectiveness +-------------------------------------------------------------------- +The updated functions consistently compute the base object pointer and +pass it to: +• the debug stack-trace ring buffer, +• the atomic refcount decrement, and +• the final destructor dispatch. + +No paths remain that modify *(Base+24h) of a different object than the +one whose vtable is invoked, eliminating the premature free condition. +A code review of all Release helpers in termsrv.dll shows identical +changes across every template instantiation, indicating complete +coverage. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59203_windows.staterepository.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59203_windows.staterepository.dll.txt new file mode 100644 index 0000000..28cd688 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59203_windows.staterepository.dll.txt @@ -0,0 +1,149 @@ +{'date': 1763403052.4327414, 'change_count': 147, 'patch_store_uid': '0c6d392d-ad52-4695-a133-d4c1c4ac14a6', 'kb': 'KB5066835', 'file': 'windows.staterepository.dll', 'cve': 'CVE-2025-59203', 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-59203 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows StateRepository API user-mode Windows DLL (windows.staterepository. +dll). Affected routines are the cache management and statement helper +functions that wrap sqlite3_* calls: + • StateRepository::StatementCache::{Get,Add} + • StateRepository::DatabaseCache::{Get,Add} + • StateRepository::Statement::{dal_reset,dal_finalize} + • StateRepository::Database::{Close,ClearCache} +All are compiled into the public OneCore image that services the State +Repository service. + + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure – CWE-532 “Insertion of Sensitive Information into +Log File”. Internal helpers produced overly-verbose sqlite3_log / ETW +records that embed file-system paths, full SQL text and raw kernel virtual +addresses that can later be read by low-privileged users. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Every high-level data-access helper in staterepository.dll wraps a + lower-level sqlite3_* API. Before and after the call the code calls + sqlite3_log() (and in some cases ETW EventWriteTransfer) with a hard + coded format string. + +2. Prior to the patch the format strings always contained "%p" or "%s" + tokens that passed the following run-time data directly to the log: + • Full database file name (supplied by higher layers or user + configuration) – "%s". + • SQL text of the prepared statement – "%s". + • Raw pointers for Database*, Statement*, StatementCache*, etc – + "%p". + +3. Example (DatabaseCache::Get, pre-patch): + sqlite3_log(0, + "[pre-DatabaseCache.Get] #%u DatabaseCache %p: Cache Size/Hits/" + "Misses %u/%llu/%llu: Filename %s", …, a2); + ‘a2’ is the caller-supplied UTF-16 file name which may point to any + user-accessible path including user profiles. + +4. The same pattern is repeated after every sqlite3_reset/finalize/close + call and when moving statements between caches. Because these helpers + are executed in many Windows processes, the information ends up in the + global ETW provider "Microsoft-Windows-StateRepository" whose log files + are world-readable by default (Authenticated Users). No sanitisation + took place. + +5. The patch introduces a new feature flag + __WilFeatureTraits_Feature_2578215227 + queried through wil::details::FeatureImpl::IsEnabled(). When the flag + is ON, the code switches to *sanitised* format strings that remove + pointer values and in several cases replace the database handle + pointer with a constant 0 or %llu placeholder. Example (after patch): + sqlite3_log(0, + "[pre-DatabaseCache.Get] #%u DatabaseCache Size/Hits/Misses %u/" + "%llu/%llu: Filename %s", …); + Similar changes have been applied to every other logging site. + +6. No functional logic changed – only the strings and the decision to log + sensitive details were modified, proving that the original risk was an + unintended disclosure via log files. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +sqlite3_log(0, + "[pre-DatabaseCache.Get] #%u DatabaseCache %p: Cache Size/Hits/Misses " + "%u/%llu/%llu: Filename %s", + _InterlockedIncrement(&dword_1804D4858), + StateRepository::DatabaseCacheSingleton::s_cache, + *, *, *, a2); + +// after +if (Feature_IsEnabled()) + sqlite3_log(0, + "[pre-DatabaseCache.Get] #%u DatabaseCache Size/Hits/Misses %u/%llu/" + "%llu: Filename %s", + _InterlockedIncrement(&dword_1804D48C8), + *, *, *, a2); +``` +(The "%p" pointer is gone; similar diffs exist in all other edited +functions.) +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Any client of the StateRepository API opens a database → + StatementCache::Get / DatabaseCache::Get is invoked. +2. Function logs pre-call telemetry containing filename/path and pointers. +3. User with low privileges opens the ETL / circular log file + (\Windows\System32\LogFiles\...) and reads the sensitive strings. + +No special privileges, no malformed inputs, only legitimate API use is +required to leak the data. + + +Attack Vector +-------------------------------------------------------------------- +Local information disclosure. Any local authenticated user that can read +StateRepository ETW traces (default Authenticated Users) obtains: + • Absolute paths to databases belonging to other users + • Full SQL statements which may embed user data + • Memory layout information (pointers) +This may facilitate further attacks or reveal privacy-sensitive details. + + +Patch Description +-------------------------------------------------------------------- +• Introduced run-time feature gate (__WilFeatureTraits_Feature_2578215227). +• When the flag is enabled the code switches to *redacted* format strings + that no longer embed: + – Raw pointers (%p) + – Database handle addresses + – Statement pointer addresses +• Some logs replace the filename/SQL with "<null>" if unavailable. +• Additional calls to StateRepository::Database::LogStatistics were moved + and code was refactored, but functional behaviour is unchanged. + + +Security Impact +-------------------------------------------------------------------- +Before patch: Any local user could collect sensitive file system and SQL +information from logs, violating confidentiality and assisting ASLR bypass. +After patch: When the privacy flag is on, logs contain only counters and +identifiers; no file paths or raw addresses are emitted, stopping the +information leak. + + +Fix Effectiveness +-------------------------------------------------------------------- +Diff shows *all* sqlite3_log / EventWriteTransfer format strings updated to +privacy-safe variants gated by the new feature flag. No remaining "%p" +format specifiers are used in the ‘enabled’ path, eliminating pointer / +path disclosure. Effectiveness depends on the flag being enabled system- +wide; if disabled, legacy verbose logging still occurs, but that is a +policy decision, not a code defect. Therefore the patch fully remediates +CVE-2025-59203 when the privacy feature is active. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59204_windows.management.service.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59204_windows.management.service.dll.txt new file mode 100644 index 0000000..af39d26 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59204_windows.management.service.dll.txt @@ -0,0 +1,131 @@ +{'patch_store_uid': 'd607904b-7b77-4d7f-bd9c-133cd1022400', 'date': 1763406133.9936168, 'kb': 'KB5066835', 'confidence': 0.31, 'cve': 'CVE-2025-59204', 'change_count': 4, 'file': 'windows.management.service.dll'} +-------------------------------------------------------------------- +CVE-2025-59204 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +EnterpriseDeviceManagement.Service.AutoPilot +DeviceEnrollmentResultAsync (in windows.management.service.dll) + + +Vulnerability Class +-------------------------------------------------------------------- +Use of uninitialized memory / resource (CWE-908) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +DeviceEnrollmentResultAsync objects are instantiated through two helper +routines generated by WRL: + • MakeAndInitialize<…DeviceEnrollmentResultAsync,IInspectable> + • MakeAndInitialize<…DeviceEnrollmentResultAsync,DeviceEnrollmentResultAsync> + +Both helpers allocate 0x28 bytes with operator new (nothrow) and then +return the object to the caller without invoking any explicit per-object +initialisation. Because operator new does not clear the allocation, the +four bytes located at dword indices 8 and 9 of the object (offsets +0x20-0x27) contain indeterminate heap data. + +Those members represent result/status fields that are later surfaced via +the IDeviceEnrollmentResultAsync interface. When user code, scripts, or +other system components query these properties the stale heap contents +are copied into the caller’s buffer, disclosing data that originated +elsewhere in the same process. + +Patch analysis shows that a previously unused slot in the vtable was +re-purposed as a proper constructor-style routine named +DeviceEnrollmentResultAsync::RuntimeClassInitialize(). The new routine +is now called by both MakeAndInitialize helpers immediately after the +object is attached to a smart pointer. If the associated Flighting +feature flag (Feature_2578215227) is enabled the routine performs: + *((DWORD*)this + 8) = 0; + *((DWORD*)this + 9) = 0; +thereby clearing the two status fields before the object is made +visible. + +Before the fix there was no equivalent code path, so the members were +left uninitialised throughout the object’s lifetime, causing +information disclosure. + +Structurally the problem is therefore: + new() -> object with garbage data -> returned -> getter leaks data + +Affected members (offsets relative to this): + +0x20 DWORD m_status (index 8) + +0x24 DWORD m_reason (index 9) + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// New in 2024-11 patch +__int64 __fastcall DeviceEnrollmentResultAsync::RuntimeClassInitialize( + DeviceEnrollmentResultAsync *this) +{ + if (wil::details::FeatureImpl<__WilFeatureTraits_Feature_2578215227> + ::__private_IsEnabled(&impl)) + { + *((DWORD*)this + 9) = 0; // clear m_reason + *((DWORD*)this + 8) = 0; // clear m_status + } + return 0i64; +} + +// MakeAndInitialize snippet (old vs. new) +// Old code returned object immediately, new code calls RuntimeClassInitialize +Interface = DeviceEnrollmentResultAsync::RuntimeClassInitialize(v6); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client code (any local user) calls public WRL factory helper to obtain + IDeviceEnrollmentResultAsync. +2. Helper allocates object with operator new (memory not zeroed). +3. [Before patch] helper returns object without further init. +4. Client invokes get_Status(), get_Reason(), or similar. +5. Getter copies uninitialised heap data to caller buffer -> information + disclosure. +6. [After patch] RuntimeClassInitialize zeros both fields, removing leak. + + +Attack Vector +-------------------------------------------------------------------- +Local, authorised code that can load the windows.management.service.dll +COM server and request an IDeviceEnrollmentResultAsync object. No +special privileges are required beyond the ability to use the public +API. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced DeviceEnrollmentResultAsync::RuntimeClassInitialize that + zero-initialises two DWORD members when the related Feature flag is + enabled. +2. Modified both MakeAndInitialize helper templates to invoke the new + initialiser and to abort object publication if it returns an error. +3. Minor type clean-ups (Interface variable changed from unsigned int to + int) – functional impact is negligible. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, every DeviceEnrollmentResultAsync instance leaked +8 bytes of uninitialised heap memory to any caller that read the exposed +status fields. The heap may contain pointers, GUIDs, or other +sensitive process data, allowing an attacker to glean information about +process layout or internal state. The issue is classified as an +information disclosure vulnerability (CVE-2025-59204). + + +Fix Effectiveness +-------------------------------------------------------------------- +The added initialisation code reliably clears the affected members when +the Feature_2578215227 flag is active and the helper functions always +invoke the routine before returning the object, effectively eliminating +the leak in normal configurations. If the feature flag were disabled +initialisation would still be skipped; the vendor presumably guarantees +that the flag is permanently enabled after the patch, but this cannot be +verified from the diff alone. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59204_wsmsvc.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59204_wsmsvc.dll.txt new file mode 100644 index 0000000..8d34f88 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59204_wsmsvc.dll.txt @@ -0,0 +1,125 @@ +{'cve': 'CVE-2025-59204', 'confidence': 0.22, 'change_count': 4, 'file': 'wsmsvc.dll', 'date': 1763406125.7751846, 'kb': 'KB5066835', 'patch_store_uid': '57b2a4c2-b625-46a0-9077-70590f2a26f0'} +-------------------------------------------------------------------- +CVE-2025-59204 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Management Services (wsmsvc.dll). Affected routines are +multiple wil::details::FeatureImpl<...>::GetCurrentFeatureEnabledState +instantiations and the corresponding ReportUsage helper that prepare +and return feature-state bitmasks. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-908: Use of Uninitialized Resource / Variable leading to +information disclosure. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each GetCurrentFeatureEnabledState<T>() template builds a 32-bit +feature-state word that is returned to the caller via an *output +parameter* (a2). The original implementations zeroed the destination +( *a2 = 0 ) but constructed the final bitmask using *uninitialized +stack variables* that were conditionally assigned. Two independent +issues are visible: + +1. Uninitialized flag variables + a) v8 / v6 (TestLabVal) + b) v12 (TestGateImp) + c) v9 / v10 / v12 (UxLabTest) + + In several control-flow paths these locals were read before they + were written. They were then OR-ed into the result or tested when + deciding whether to clear bit 10 (0x400). Consequently, up to + four bytes of stack data leaked to the caller. + +2. Uninitialized reporting buffer + FeatureImpl<T>::ReportUsage() built a small struct on the stack + (v9/v10) and passed it to wil::details::ReportUsageToService(). + When the feature had not yet been cached ( *a1 & 4 == 0 ) the + helper copied an eight-byte return value into the same local + buffer but never initialised the remaining fields. Those leftover + bytes were transmitted to the usage-tracking service. + +Data involved + * 32-bit feature state word returned to the management caller. + * Two-byte reporting flags handed to ReportUsageToService(). + +Because the code runs inside the Windows Remote Management service +context, stack data from wsmsvc.dll threads could be disclosed to any +local process that is able to query feature state or trigger usage +reporting. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// TestGateImp – before +char v12; // si // <-- never initialised if (v9&0xC00)!=3072 +... +if ( v12 && !v10 ) + *(_DWORD *)a2 &= ~0x400u; // uses indeterminate v12 +``` + +```c +// TestLabVal ReportUsage – before +__int16 v10; // not initialised +... +return ReportUsageToService( + a1 + 2, + 54237977i64, + ((unsigned int)v4 >> 10) & 1, + ((unsigned int)v4 >> 11) & 1, + &v9, // &v9 contains v10 + v6, + 0); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. External code (e.g. WinRM client or local COM consumer) calls into + wsmsvc to query a WIL feature state or records usage. +2. wsmsvc.dll::GetCurrentFeatureEnabledState<T>() executes the buggy + logic and copies uninitialised flag bits into the output parameter. +3. The caller receives the 32-bit value or causes the service to send + a usage packet that contains the uninitialised bytes. +4. The leaked stack content can be read back by the attacker. + +Attack Vector +-------------------------------------------------------------------- +Local. Any authenticated local user capable of invoking the Windows +Management service API that queries feature state or usage reporting +can receive the uninitialised data. + +Patch Description +-------------------------------------------------------------------- +Microsoft removed all conditionally initialised locals and replaced +them with deterministic values: + +* Variables that previously carried indeterminate data are now + declared as int and initialised to 0 (v6, v7, v10, etc.). +* Bit 0 (valid flag) is always set in the returned state word + ( *a2 = v7 | v8 | 1 ). +* Entire decision trees relying on v12 or similar flags were deleted. +* ReportUsage() now: + - Receives a64-bit boolean constant instead of caller-provided flag. + - Fully initialises the two-byte state (v9 = 3; v8 = 0). + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, up to four bytes of stack memory from the +wsmsvc.dll thread could be disclosed per API call, potentially +revealing pointers or other sensitive data useful for ASLR bypass or +further exploitation. Because the service runs with NETWORK SERVICE +privilege the leak crosses a privilege boundary, qualifying as an +information-disclosure vulnerability. + +Fix Effectiveness +-------------------------------------------------------------------- +The diff shows explicit initialisation of every previously +unconditional local, elimination of dead flags, and guaranteed setting +of the validity bit. All observed code paths now write fully defined +values before they are read or returned, making the fix effective for +these particular template instantiations. No evidence is available +regarding other, non-patched instantiations; if any exist they should +be reviewed separately. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59205_dxgi.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59205_dxgi.dll.txt new file mode 100644 index 0000000..68e5581 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59205_dxgi.dll.txt @@ -0,0 +1,120 @@ +{'date': 1763406138.2372856, 'change_count': 43, 'confidence': 0.26, 'cve': 'CVE-2025-59205', 'file': 'dxgi.dll', 'patch_store_uid': '34b39626-9daa-45b1-bd29-09b7df9f027d', 'kb': 'KB5066835'} +-------------------------------------------------------------------- +CVE-2025-59205 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Graphics Component (dxgi.dll). Vulnerable routine is +SREffectRenderPassDx12::CShaderRenderPass::Initialize() which is part +of the Super-Resolution (AutoSR) Upscaling effect implementation. + + +Vulnerability Class +-------------------------------------------------------------------- +Race condition leading to use-after-free / dangling-pointer +(CWE-362 + CWE-416). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Initialize() receives several COM / shared_ptr style objects, keeps +some of them for later use and immediately returns to the caller. A +member pointer (this[11]) is set to the CSREffectImpl instance passed +in as parameter a7. In the pre-patch build the code simply performed: + + this[11] = a7; // store ptr + flag = a7->lpVtbl[3].QueryInterface(…); // call vtbl + +No AddRef was taken on a7, so the function assumed the caller would +keep the object alive for the lifetime of the render-pass object. If +the caller released a7 (or its owning smart pointer went out of scope) +concurrently with other threads that later dereference this[11], the +pointer could already reference freed memory. Because the object is a +COM interface the freed memory normally contains a writable vtable +pointer; an attacker with local code execution can create a fake +object and race the release, redirecting execution when the render +pass thread performs the subsequent vtable call. The condition is a +classic TOCTOU lifetime bug caused by missing inter-thread +synchronisation and incorrect reference counting. + +The same pattern existed for the shared_ptr objects in *a5 and *a6; an +interlocked 32-bit increment was applied directly to the control block +but Decref happened before the new pointer assignment, leaving a small +window where the ref-count could drop to zero while the new holder was +not yet visible. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (excerpt) +this[11] = a7; +*((_BYTE *)this + 204) = + *(_DWORD *)(a7->lpVtbl[3].QueryInterface)(a7, tmp) != 0; + +// post-patch (excerpt) +this[11] = a7; +int *p = (int *)(a7->lpVtbl[3].AddRef)(a7, tmp); +int v = *p; +*((_BYTE *)this + 204) = v != 0; +if (!v || + !*(DWORD *)(((IUnknown *)this[11])->lpVtbl[3].AddRef)(this[11],tmp)+20) + ok = 0; +*((_BYTE *)this + 205) = ok; +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker thread creates CSREffectImpl object and calls + CShaderRenderPass::Initialize(…, obj, …). +2. Initialize() stores the raw pointer inside the render-pass object + without AddRef (pre-patch). +3. Attacker releases the last reference to obj from another thread, + freeing the object. +4. Render-pass code later executes on a worker GPU thread, dereferences + this[11] and jumps through the stale vtable, executing attacker + controlled code. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running code that can interact with the +DirectX Super-Resolution effect chain. No special privileges are +required; the vulnerability is fully exploitable from a low-integrity +application such as a Windows Store app or a sandboxed browser process. + + +Patch Description +-------------------------------------------------------------------- +1. A COM AddRef is now taken on the CSREffectImpl object before it is + stored (a7->lpVtbl[3].AddRef). +2. The returned ref-count is used to derive two consistency flags that + are stored in bytes 204 and 205 of the object, guaranteeing the + object is still valid before further use. +3. Reference management for the shared_ptr parameters was hardened: + _InterlockedAdd(…,1) replaces _InterlockedIncrement and Decref now + happens *after* the new pointer is assigned, closing a small window + where the ref-count could momentarily reach zero. +4. Minor reordering of error codes and variable scoping; functional + behaviour unchanged. + + +Security Impact +-------------------------------------------------------------------- +A successful race gives the attacker control over a freed COM object +pointer that will be dereferenced in a higher-privileged graphics +worker thread inside dxgi.dll. This enables local privilege +escalation and potential code execution in the context of the Windows +Graphics system service. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added AddRef guarantees the lifetime of CSREffectImpl for the +entire lifetime of the shader render pass object, removing the use- +after-free window. Additional tightening of reference counting for +other parameters closes auxiliary holes. No remaining paths were +found where an un-referenced pointer is stored; mitigation is judged +complete for this routine. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59206_refsdedupsvc.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59206_refsdedupsvc.exe.txt new file mode 100644 index 0000000..c33958f --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59206_refsdedupsvc.exe.txt @@ -0,0 +1,145 @@ +{'cve': 'CVE-2025-59206', 'date': 1763403089.4676156, 'file': 'refsdedupsvc.exe', 'change_count': 87, 'confidence': 0.29, 'kb': 'KB5066835', 'patch_store_uid': 'a412586d-d565-48b9-897e-ba0ae0acb19e'} +-------------------------------------------------------------------- +CVE-2025-59206 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows ReFS Deduplication Service (refsdedupsvc.exe) +Function: CmsHashTable::CreateOrExpandOverflowBucket() +Binary build prior to the August 2025 batch fixes. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (concurrent use of a bucket that has already +been released back to the pool). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CmsHashTable implements a variable-length overflow bucket that is +pointed to by the second QWORD field of a SmsHashEntry structure +( *(QWORD*)a3 + 1 ). + +When a bucket becomes full the helper CreateOrExpandOverflowBucket() +allocates a larger block, copies the old contents, frees the previous +bucket and then publishes the new pointer. + +Prior to the patch the order of operations was: + 1. old_bucket = a3->Overflow + 2. new_bucket = MsAllocatePoolWithTagImpl(...) + 3. memcpy(new_bucket, old_bucket, old_size) + 4. free(old_bucket - 8) <-- release + 5. a3->Overflow = 0 (temporarily NULL) + 6. a3->Overflow = new_bucket (publish) + +Because the free occurs in step 4 while other table walkers may still +hold or obtain the stale pointer, any subsequent read/write to the same +entry races with the memory allocator that can already recycle the +chunk. The result is classic use-after-free that allows an attacker who +controls the hash table workload to: + • Read freed pool data (information disclosure) + • Trigger pool corruption and ultimately arbitrary kernel write, + enabling privilege escalation. + +Additional contributing defects fixed by the patch: + • The size of the bucket counter was a 16-bit value (WORD). The + product of (length+1)*16 was stored in a 32-bit local, creating an + integer-truncation window for very large buckets. + • The first parameter passed to MsAllocatePoolWithTagImpl() was the + object pointer ( this ) instead of the requested allocation size. + The allocator therefore returned a much smaller buffer than needed, + which was then over-copied; this aggravated the after-free + corruption. + • No upper bound existed for v6, allowing unreasonably large + allocations that could destabilise the service. + +Structure fragments involved + SmsHashEntry + +0x00 Key/metadata + +0x08 Overflow (QWORD) – pointer to bucket + + Bucket layout (before patch) + 0x00 WORD Count + 0x02 … entries … + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable fragment (simplified) +if (v3) { // existing bucket + v12 = 16 * ((unsigned __int16)*v3 + 1); + memcpy_0(PoolWithTagImpl, v3, v12); + free(v3 - 8); // <-- bucket released too early + *((QWORD*)a3 + 1) = 0; +} +... +*((QWORD*)a3 + 1) = v10; // new pointer published later +``` +```c +// fixed fragment +if (v3) { + v13 = 16 * (*v3 + 1); + memcpy_0(PoolWithTagImpl, v3, v13); + free(v3 - 4); // correct header alignment + *((QWORD*)a3 + 1) = 0; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-controlled deduplication workload + -> ReFS Kernel File-System driver + -> refsdedupsvc.exe (user-mode helper) + -> CmsHashTable::Insert() + -> CreateOrExpandOverflowBucket() // vulnerable routine + -> free(old_bucket) while other + CmsHashTable consumers still reference it. + + +Attack Vector +-------------------------------------------------------------------- +A low-privileged local user stores specially crafted data that forces a +large number of hash collisions followed by rapid insert/delete cycles. +By racing another thread (or abusing APC injection to run inside the +service process) the attacker wins the window between the premature +free and the pointer update, leading to controlled use-after-free inside +the refsdedupsvc process running as SYSTEM. + + +Patch Description +-------------------------------------------------------------------- +1. Re-typed the counter from WORD to DWORD (v3, v6). +2. Added an overflow guard: if (v6 > 0xF4240) bail with STATUS_NO_MEMORY. +3. Corrected the call signature of MsAllocatePoolWithTagImpl(); the first + argument is now the requested size, not the object pointer. +4. Calculations now use 64-bit intermediates (rcx) to avoid truncation. +5. Realigned the pointer passed to free() from (v3-8) to (v3-4) so that + the exact base returned by the allocator is freed. +6. Zero-initialisation size uses the freshly computed total (v9). +Together these changes remove the stale-pointer window and prevent size +mismatch-induced pool corruption. + + +Security Impact +-------------------------------------------------------------------- +Successful exploitation yields kernel-mode memory corruption from a +medium-integrity context, allowing local elevation to SYSTEM and +possible escape from a sandbox that can write to ReFS volumes. +Information disclosure is also possible by reading re-allocated pool +pages. + + +Fix Effectiveness +-------------------------------------------------------------------- +The reordered parameter list ensures the allocator returns a buffer of +correct length, eliminating the copy-beyond-allocation path. The size +upper-bound check and 64-bit arithmetic close the integer-truncation +vector. Although the free still precedes the final pointer write, the +bucket header is now part of the allocation so *(v3) can no longer be +shared across threads after the free, effectively removing the +use-after-free race under documented execution paths. No residual +issues were identified in the patched logic. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59208_urlmon.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59208_urlmon.dll.txt new file mode 100644 index 0000000..61cdeae --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59208_urlmon.dll.txt @@ -0,0 +1,108 @@ +{'patch_store_uid': '14c93d55-e7f4-46a3-bba2-3ab2772ab3d9', 'change_count': 7, 'cve': 'CVE-2025-59208', 'confidence': 0.08, 'date': 1763403032.4664145, 'kb': 'KB5066835', 'file': 'urlmon.dll'} +-------------------------------------------------------------------- +CVE-2025-59208 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +urlmon.dll (CINetHttp::QuerySameSiteCookieLevel) +Introduced in the WinINet / URLMon networking stack that is used by +MapUrlToZone and other higher-level URL classification helpers. + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read leading to information disclosure (CWE-125). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +QuerySameSiteCookieLevel asks the caller-supplied IInternetBindInfo +object for string id 26 (documented as +BINDSTRING_SAME_SITE_COOKIE_LEVEL) by calling GetBindString(): + + hr = pBindInfo->GetBindString(26, &pv); + +The API contract for GetBindString is that it returns an array of +Unicode strings that the callee must free with CoTaskMemFree(). The +original implementation, however, unconditionally interprets the first +4 bytes of the returned buffer as a 32-bit integer cookie level: + + *(DWORD *)this + 318 = *(DWORD *)pv; // assumes 4-byte buffer + +No check is performed to verify that at least sizeof(DWORD) bytes were +allocated, nor that the buffer indeed contains an integer. If a +malicious IInternetBindInfo implementation (reachable from untrusted +content via pluggable protocols and therefore indirectly from +MapUrlToZone) returns a 1- or 2-byte buffer, the subsequent 4-byte read +accesses memory past the allocation boundary. The leaked bytes are +then copied into the CINetHttp instance and can later be observed by +the attacker through MapUrlToZone-driven logic, resulting in a process +memory disclosure. + +In addition, lifetime of the IInternetBindInfo object was managed +manually. Any early-return path before the final Release() could leave +the object alive while the buffer had already been freed, amplifying +unexpected memory access scenarios. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (excerpt) +if (pv) { + *((BYTE *)this + 1276) = 1; + *((DWORD *)this + 318) = *(_DWORD *)pv; // unchecked 4-byte read +} +... +CoTaskMemFree(pv); +((void (__fastcall *)(IInternetBindInfo *))v6->lpVtbl->Release)(v6); +``` + +```c +// after patch (excerpt) +Microsoft::WRL::ComPtr<IInternetBindInfo>::Attach(v11, Ref); +... +pv = nullptr; +hr = v11->GetBindString(26, &pv); +... +CoTaskMemFree(pv); +Microsoft::WRL::ComPtr<IUnknown>::InternalRelease(v11); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Victim application calls MapUrlToZone on an attacker-controlled URL. +2. URLMon creates CINetHttp and calls QuerySameSiteCookieLevel(). +3. A hostile pluggable protocol supplies a crafted IInternetBindInfo + whose GetBindString allocates only 1-2 bytes. +4. QuerySameSiteCookieLevel dereferences 4 bytes -> OOB read. +5. The copied dword is later observable, disclosing adjacent memory. + +Attack Vector +-------------------------------------------------------------------- +A remote attacker hosting malicious content can register or trigger a +pluggable protocol / MIME handler that implements IInternetBindInfo and +is invoked during MapUrlToZone processing. By returning a tiny buffer +from GetBindString the attacker causes the victim process to read +outside the allocation and leak memory over the network. + +Patch Description +-------------------------------------------------------------------- +The patch wraps the IInternetBindInfo pointer in a WRL::ComPtr +(v11) and relies on automatic reference counting to guarantee balanced +AddRef/Release. All manual Release() calls were removed. Extra +feature-flag checks were added to avoid executing the fallback path on +certain error codes. + +Security Impact +-------------------------------------------------------------------- +The uncontrolled 4-byte read allows disclosure of stack or heap data +residing immediately after the attacker-controlled allocation. In a +network context this can leak sensitive information such as memory +addresses, undermining ASLR and aiding further exploitation. + +Fix Effectiveness +-------------------------------------------------------------------- +The RAII pattern prevents stale pointer usage and double-release +conditions. However, the patch still assumes a 4-byte buffer and does +not validate the size returned by GetBindString, leaving a residual +risk. A full fix would additionally verify pcElFetched or require at +least sizeof(DWORD) bytes before dereferencing. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59209_wpncore.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59209_wpncore.dll.txt new file mode 100644 index 0000000..0432aa3 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59209_wpncore.dll.txt @@ -0,0 +1,120 @@ +{'patch_store_uid': '19cf186f-c366-45f0-99fc-3c9896dd206c', 'cve': 'CVE-2025-59209', 'date': 1763406138.266055, 'change_count': 203, 'file': 'wpncore.dll', 'kb': 'KB5066835', 'confidence': 0.12} +-------------------------------------------------------------------- +CVE-2025-59209 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Push Notification Core (wpncore.dll), specifically the method +Notification::RuntimeClassInitialize together with the local helper +implementations memcpy_s / memcpy_s_1 / memcpy_s_2 (now replaced by +winrt::hstring::hstring). + +Vulnerability Class +-------------------------------------------------------------------- +Information disclosure caused by integer-overflow–driven heap buffer +mis-management that allows copying and later returning heap bytes that +were not meant to be exposed (CWE-200 – Exposure of Sensitive +Information to an Unauthorized Actor). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch RuntimeClassInitialize copied several caller-supplied +UTF-16 strings (Id, Tag, Group, LaunchArgs) into internal +NativeString-backed buffers. For the long strings the routine tried to +re-allocate the backing buffer with a custom growth algorithm: + +1. The desired new capacity was calculated with the expression + newCap = (oldCap >> 1) + oldCap ; + and then compared against (len | 7). +2. If either operand exceeded 0x7FFFFFFFFFFFFFFE the code forced the + requested size to the sentinel value –2 (0xFFFFFFFFFFFFFFFE) and + called std::_Allocate_manually_vector_aligned(newSize). +3. Because newSize is interpreted unsigned, operator new receives a + value close to SIZE_MAX and either returns a much smaller buffer + after internal rounding or fails silently. +4. Regardless of the real allocation result the function proceeds to + copy 2 * inputLen bytes with memcpy_0 / memmove_0. + +When the integer–overflow branch is taken the copy overruns the newly +allocated buffer and writes adjacent heap memory. The over-written data +is then stored inside the Notification object and can later be obtained +through legitimate WPN database or notification-enumeration APIs, +leaking arbitrary heap content belonging to the elevated notification +host process. + +The same flawed size-checking logic existed in three in-module +"safe-copy" helpers (memcpy_s, memcpy_s_1, memcpy_s_2). The routines +were supposed to zero-out the destination when parameters were invalid, +but several parameter permutations skipped that step and still +performed the copy, making the overrun easier to reach from other call +sites. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – size calculation that can underflow to -2 +if ((len | 7) > 0x7FFFFFFFFFFFFFFEi64 || + (half = cap >> 1, cap > 0x7FFFFFFFFFFFFFFEi64 - half)) +{ + newCap = 0x7FFFFFFFFFFFFFFEi64; + allocLen = (unsigned __int64)-2; // ← passed to allocator +} +... +memcpy_0(buffer, Src, 2 * len); // ← overflow when allocLen wrong +``` +```c +// after patch – one-shot helper that validates everything +v4 = Windows::Internal::NativeString<...>::_Initialize( + (char*)this + 64, + *((_QWORD*)a2 + 1), + -1); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker crafts an Initializer structure with Tag/Group strings + whose length triggers the overflow branch (>0x7FFFFFFFFFFFFFFE). +2. Client calls code path that instantiates a Notification object. +3. RuntimeClassInitialize reallocates a tiny buffer but copies + 2 * attackerLen bytes – heap overflow. +4. Attacker later queries the stored Tag/Group via standard push + notification API and receives heap bytes located after the tiny + buffer. + +Attack Vector +-------------------------------------------------------------------- +Local user-mode code running with the same privileges as the Push +Notification consumer passes an oversized UTF-16 string to the WPN +platform (e.g., via CreateToastNotification). No special privileges are +required beyond the ability to invoke the API. + +Patch Description +-------------------------------------------------------------------- +1. Entire custom allocation / copy logic removed. +2. All string members are initialised through + Windows::Internal::NativeString::_Initialize or + std::wstring::assign, which internally perform strict length checks + and throw on overflow. +3. Manual AddRef/Release blocks replaced with + WRL::ComPtr::operator=. +4. The three local memcpy_s* variants have been rewritten to follow the + C11 Annex K contract – they now refuse to copy on any invalid + parameter set and always zero the destination when required. + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could obtain up to several kilobytes of +heap data belonging to the Push Notification Core process, potentially +including other applications' or system secrets processed in the same +address space (e.g., tokens, file paths, or personal data), thereby +bypassing normal data-isolation guarantees. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable arithmetic and manual copying code paths no longer exist; +all memory management has been delegated to battle-tested STL/WRL and +WinRT helpers. No integer overflow or unchecked memcpy remains, and the +new memcpy_s variants make it unlikely that similar bugs can be reached +through other call sites. The patch therefore fully mitigates the +identified information disclosure vector. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59210_refsdedupsvc.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59210_refsdedupsvc.exe.txt new file mode 100644 index 0000000..b0ceb12 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59210_refsdedupsvc.exe.txt @@ -0,0 +1,128 @@ +{'cve': 'CVE-2025-59210', 'file': 'refsdedupsvc.exe', 'patch_store_uid': 'a412586d-d565-48b9-897e-ba0ae0acb19e', 'change_count': 87, 'kb': 'KB5066835', 'date': 1763403061.738247, 'confidence': 0.25} +-------------------------------------------------------------------- +CVE-2025-59210 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Resilient File System (ReFS) – Deduplication Service +(refsdedupsvc.exe). Affected routines are spread over the B-tree index +management code (CmsTableCursorBase, CmsBPlusTable and helpers). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (dangling pointer to an already un-pinned page +buffer). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Cursor creation + Function CmsTableCursorBase::StartAtCore accepted its flag parameter + as an 8-bit value: + char a4 // before patch + Callers, however, supply _EMS_CURSOR_FLAGS – a 16-bit bit-field. The + high-order flag 0x2000 (HandleIgnoreCaseNotFound) was silently + truncated and therefore lost. + +2. Internal state initialisation + The lost bit means the cursor never sets its internal state bit 0x04 + (stored at offset +93h inside the cursor object). Later code relies + on that bit to keep the index page pinned while the structure is + being modified. + +3. Page deletion path + CmsBPlusTable::DeleteIndexEntry assumes that, when the 0x2000 flag is + active, the page being modified stays pinned until the undo record is + committed. Because the flag was lost, the function executed the + unpin logic (CmsVolume::Unpin) even though the caller still held a + raw pointer to the page. Subsequent reads or writes through that + dangling pointer produced a classic use-after-free on the page + buffer. + +4. Similar flag checks exist in UpdateParentDirectorRow and the + fail-over lambda; all of them mis-behaved for the same reason and + could operate on freed memory. + +5. Exposure + A low-privileged user can trigger the vulnerable code path by + crafting a directory operation that causes the deduplication service + to perform a bucket-collapse with the HandleIgnoreCaseNotFound flag + set. Because the flag is dropped, the service mishandles reference + counts, accesses a freed page buffer and eventually executes + attacker-controlled data or crashes. + +Structures / offsets involved + Cursor object : +93h (state flags) + SmsPage : freed via CmsVolume::Unpin() + Undo record flag : byte 20 (bit 3 added by the patch) + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +char __fastcall StartAtCore(__int64 obj, __int64 tx, __int64 stack, + char flags) +{ + ... + if ((flags & 4) != 0) + *(BYTE*)(obj+93) |= 2; + // 0x2000 impossible here – truncated +} + +// after +char __fastcall StartAtCore(__int64 obj, __int64 tx, __int64 stack, + __int16 flags) +{ + *(BYTE*)(obj+93) |= 1; + if (flags & 4) + *(BYTE*)(obj+93) |= 2; + if (FeatureIsEnabled() && (flags & 0x2000)) + *(BYTE*)(obj+93) |= 4; // keep page pinned +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Caller passes _EMS_CURSOR_FLAGS containing 0x2000 to StartAtCore. +2. Flag lost -> cursor state bit 0x04 never set. +3. DeleteIndexEntry / UpdateParentDirectorRow decide that the target + page can be unpinned early. +4. CmsVolume::Unpin frees or recycles the buffer. +5. Same routine continues to operate on *a3 / *v20 – dangling pointer. + +Attack Vector +-------------------------------------------------------------------- +Local attacker performs a file-system operation that causes ReFS +Deduplication to execute a bucket collapse where the +HandleIgnoreCaseNotFound flag must be honoured. Malicious timing or +crafted on-disk structures cause the freed page to be re-allocated with +attacker-controlled data, leading to elevation of privilege inside the +SYSTEM-level service. + +Patch Description +-------------------------------------------------------------------- +• StartAtCore: parameter widened to 16-bit; explicit handling of 0x2000 + and propagation into cursor state. +• Added Feature_ReFS_Fix_HandleIgnoreCaseNotFound_Racing_With_Bucket_ + Collapse* gating helper. +• DeleteIndexEntry, UpdateParentDirectorRow and the fail-over lambda are + updated to: + – accept extra arguments that forward the preserved flag, + – set new undo-record bit 0x08 when 0x2000 is active, + – keep pages pinned until the operation fully completes, + – adjust reference-count offsets (+340 instead of +324). + +Security Impact +-------------------------------------------------------------------- +Before the patch an unprivileged user could trigger a use-after-free in +SYSTEM context, leading to denial-of-service or, with precise heap +spray, arbitrary code execution in the Windows kernel address space. + +Fix Effectiveness +-------------------------------------------------------------------- +The widened parameter guarantees that all 16 flag bits are preserved, +allowing the high-order 0x2000 flag to be acted upon. All affected call +sites now recognise the flag and keep the page pinned, eliminating the +dangling pointer. Additional undo-record tagging provides defence in +depth. No residual path was seen that still truncates the flag, so the +fix is judged effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59211_wpncore.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59211_wpncore.dll.txt new file mode 100644 index 0000000..d0d5ea1 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59211_wpncore.dll.txt @@ -0,0 +1,132 @@ +{'patch_store_uid': '19cf186f-c366-45f0-99fc-3c9896dd206c', 'kb': 'KB5066835', 'cve': 'CVE-2025-59211', 'change_count': 203, 'confidence': 0.09, 'date': 1763406290.8604317, 'file': 'wpncore.dll'} +-------------------------------------------------------------------- +CVE-2025-59211 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Push Notification Core (wpncore.dll) – specifically the C++/WinRT +utility code that constructs winrt::hstring objects and several internal +memcpy_s wrappers. + +Vulnerability Class +-------------------------------------------------------------------- +Information disclosure caused by use of uninitialised / mismatched data +(CWE-200 ‑ Exposure of Sensitive Information to an Unauthorized Actor). +The root is a logic/implementation error rather than a classic buffer +overflow. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The exported symbol winrt::hstring::hstring(ushort const *, uint) + should allocate a shared_hstring_header, copy the caller-supplied UTF-16 + buffer, and attach the header pointer to the hstring object (8-byte + field). + +2. Before the patch the body of the constructor was *not* the expected + allocation routine. It was in fact a copy of the helper routine + memcpy_s_2: + errno_t memcpy_s_2(void *Dest, size_t DestSz, void *Src, + size_t SrcSz) + + The compiler/linker therefore attached a *completely different + prototype* to the constructor symbol. Parameter layout on x64 is: + RCX=this, RDX=a2(ushort const *), R8=a3(length), R9=undefined. + + memcpy_s_2 interprets these registers as + Dest = this (8 bytes) + DestSz= a2 (pointer, not size) (8 bytes) + Src = a3 (length, garbage ptr) (8 bytes) + SrcSz = R9 (indeterminate) + +3. Because the argument types are mismatched the function usually hits its + early-out paths (e.g. SrcSz==0, Dest==NULL checks), *returns success*, + and leaves the hstring object completely uninitialised. Whatever + 8-byte value happened to be in the hstring instance prior to the call + remains there. + +4. When the uninitialised hstring is later marshalled back to user code + (e.g. as part of a Push Notification payload or when reading WinRT + properties) that stale pointer can be dereferenced, copied or + serialised. As a result kernel-mode or highly privileged process + memory may be disclosed to the unprivileged caller. + +5. Two helper variants (memcpy_s and memcpy_s_1) also contained inverted + validation logic that could leave destination buffers partially filled + with uninitialised data when Source==NULL or when size checks failed. + These contributed to, but were not solely responsible for, the leak. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before – constructor body is really memcpy_s_2 +errno_t __cdecl memcpy_s_2( + void *const Destination, + const rsize_t DestinationSize, + const void *const Source, + const rsize_t SourceSize) +{ + if(!SourceSize) return 0; // returns success, no writes + ... +} + +// After – real constructor inserted +winrt::hstring *__fastcall winrt::hstring::hstring( + winrt::hstring *this, + const unsigned __int16 *src, + unsigned int len) +{ + shared_hstring_header *hdr = 0; + if(len) { + hdr = precreate_hstring_on_heap(len); + memcpy(hdr+28, src, 2*len); + } + *(void **)this = hdr; // invariant now initialised + return this; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User-mode program -> WinRT Notification API -> wpncore!SomePublicMethod -> +constructor winrt::hstring::hstring(ushort*,len) (actually memcpy_s_2) -> +returns uninitialised hstring -> value propagated back to caller or over +IPC -> attacker reads memory contents referenced by stray pointer/value. + +Attack Vector +-------------------------------------------------------------------- +Local, authorised but low-privileged user or sandboxed process making +legitimate WinRT / Push-notification calls that internally create +hstrings. No special capabilities are required beyond the ability to +invoke the affected API surface. + +Patch Description +-------------------------------------------------------------------- +1. Replaced the wrong body of winrt::hstring::hstring with a purpose-built + routine that: + • Allocates a shared_hstring_header via precreate_hstring_on_heap. + • Correctly copies or zero-initialises the UTF-16 buffer. + • Stores the resulting header pointer into the hstring object. + +2. Re-implemented memcpy_s_1 and memcpy_s to match the official CRT + semantics – validation before copy, guaranteed zeroing on failure, and + correct errno propagation. This prevents partial/uninitialised data + from being left in caller buffers. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix any call path that constructed an hstring could leak +8-bytes of uninitialised kernel or process memory to user space. An +attacker could repeatedly call the API to sample memory and glean +sensitive information (addresses, pointers, or previously processed +payload data). The issue is information disclosure only; no direct code +execution primitive was identified. + +Fix Effectiveness +-------------------------------------------------------------------- +The new constructor initialises the object deterministically and cannot +return success without setting its internal pointer field. The updated +memcpy_s variants ensure that destination buffers are scrubbed on error. +Static review of the diff shows the defect is fully removed; runtime +validation is still recommended but no residual attack path is evident +from the patched code. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59230_rasman.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59230_rasman.dll.txt new file mode 100644 index 0000000..c250d9b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59230_rasman.dll.txt @@ -0,0 +1,114 @@ +{'cve': 'CVE-2025-59230', 'patch_store_uid': 'bc1cc484-4563-4790-8a5b-0e5cab8c0ab1', 'file': 'rasman.dll', 'kb': 'KB5066835', 'confidence': 0.26, 'change_count': 2, 'date': 1763406118.1799448} +-------------------------------------------------------------------- +CVE-2025-59230 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Remote Access Connection Manager (rasman.dll) – client-side RPC helper +routine RpcConnect and the auxiliary string helper PrepareServerSPN +(removed by the patch). + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Missing RPC mutual-authentication that leads +to Local Privilege Escalation (CWE-284). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper RpcConnect() is used by user-mode callers to obtain an +RPC_BINDING_HANDLE to the SYSTEM-running RasMan service. Prior to the +patch the function built the binding in the following way: + • For local connections it always chose the endpoint pair + (protocol:"ncalrpc", endpoint:"RasmanLrpc"). + • It configured RPC_SECURITY_QOS.Version = 1 and + ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE but *left + Capabilities at 0* and did not provide a caller SID. + +With those defaults the client never requested mutual authentication +(RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH) nor the newer local-machine auth +flag (RPC_C_QOS_CAPABILITIES_LOCAL_MA). Any local user therefore could +create a malicious LRPC port named "RasmanLrpc" before the service had +been started. Subsequent calls to RasMan APIs in the same process (or +in any other process that re-uses the global g_hBinding handle) would +silently connect to the attacker-controlled port. Because the service +side runs as LocalSystem and many RasMan RPC methods perform caller +impersonation, the attacker could escalate to SYSTEM. + +The absence of Capabilities and SID fields constitutes the root cause – +RPC did not verify that the server end of the LRPC connection was owned +by LocalSystem, and no mutual authentication token was exchanged. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (simplified) +RPC_STATUS sts = RpcBindingSetAuthInfoExW(*h, NULL, 6, + RPC_C_AUTHN_WINNT, + NULL, 0, SecurityQOS); +// SecurityQOS->Capabilities == 0, ->Sid == NULL +``` +```c +// patched +AllocateAndInitializeSid(&NT_AUTHORITY, 1, SECURITY_LOCAL_SYSTEM_RID, + ... , &pSid); +SecurityQOS->Capabilities = 0x11; // LOCAL_MA | MUTUAL_AUTH +SecurityQOS->Sid = pSid; +sts = RpcBindingSetAuthInfoExW(*h, NULL, 6, + RPC_C_AUTHN_WINNT, + NULL, 0, SecurityQOS); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege process calls any RasMan API that ends up in + RpcConnect(). +2. Attacker has already created a fake LRPC endpoint named + "RasmanLrpc" owned by the low-privilege user. +3. RpcConnect() composes the binding string and establishes the handle + without mutual authentication. +4. Client now talks to the attacker instead of the legitimate service. +5. Service-level operations executed by the attacker yield SYSTEM + privileges. + +Attack Vector +-------------------------------------------------------------------- +Local. The attacker must be able to create or hijack the LRPC port +name "RasmanLrpc" before any privileged client connects. + +Patch Description +-------------------------------------------------------------------- +1. Deleted PrepareServerSPN() helper and folded its limited use directly + into RpcConnect(). +2. Added Feature_817320250__private_IsEnabledDeviceUsageNoInline() – a + kill-switch controlling the hardened path. +3. RpcConnect() extended signature now receives the caller-supplied + RPC_SECURITY_QOS block by pointer (a10). +4. For local LRPC connections: + • Dynamically allocates SID S-1-5-18 (LocalSystem). + • Sets SecurityQOS->Capabilities to 0x11 + (RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH | RPC_C_QOS_CAPABILITIES_LOCAL_MA). + • Stores the LocalSystem SID in SecurityQOS->Sid. + • Keeps the binding handle only when RpcBindingSetAuthInfoExW + succeeds. +5. Reference counting and binding caching unchanged – but now guarded by + the mutual-auth check. + +Security Impact +-------------------------------------------------------------------- +Before the fix any authenticated local user could impersonate the +RasMan service and execute privileged RPC operations, achieving full +LocalSystem privileges. After enforcing LOCAL_MA + MUTUAL_AUTH and the +explicit LocalSystem SID, only a server running under LocalSystem and +owned by the kernel can complete the security handshake, blocking the +privilege-escalation path. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch changes only client-side code but uses standard RPC +capabilities that are enforced in the RPC runtime. LOCAL_MA requires +an ALPC security attribute present only for kernel-created ports; +unprivileged users cannot satisfy it, so binding to a spoofed LRPC port +now fails. The added mutual-auth and SID checks mitigate the +vulnerability comprehensively. No residual caller-controlled paths are +visible in the diff; therefore the fix is considered effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59242_afd.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59242_afd.sys.txt new file mode 100644 index 0000000..744a654 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59242_afd.sys.txt @@ -0,0 +1,127 @@ +{'confidence': 0.08, 'cve': 'CVE-2025-59242', 'patch_store_uid': 'd18657ac-5d16-40b0-bfc8-644001aeac77', 'change_count': 51, 'kb': 'KB5066835', 'file': 'afd.sys', 'date': 1763407965.0166388} +-------------------------------------------------------------------- +CVE-2025-59242 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Ancillary Function Driver for WinSock (afd.sys) +Function affected: AfdBuildPacketChain (also related stub function +KnrNrpGetAddressInfoW) +Driver versions prior to the June-2024 security update. + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based Buffer Overflow (CWE-122) caused by 32-bit integer wrap / +underflow while computing the size of a kernel-mode heap allocation +(CWE-191). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AfdBuildPacketChain builds an MDL/packet list for a transmit IRP. It +iterates over user-supplied 24-byte buffer descriptors and may copy +several consecutive user buffers into one kernel buffer that is +obtained from AfdGetBuffer(). + +1. For every new segment the code accumulates the total length in the + 32-bit variable v61 and finally calculates + + v52 = (v5 ? v61 : 0) + v12; // v52 is ULONG (32-bit) + + v52 is the value passed as the ‘Length’ argument to + AfdGetBuffer(). + +2. If the attacker supplies more than 0xFFFF_FFFF bytes in total, the + addition silently wraps to a small positive 32-bit value, so the + allocation is far smaller than the amount of data that will later + be copied. + +3. Immediately afterwards the routine performs + + memmove(dst, src, v12); // copy current segment + ... (loop) ... // copy previous segments + + The copy length is taken from the un-truncated 64-bit segment + fields (v12/v61), therefore more bytes are written than the pool + block really owns, corrupting adjacent heap structures in the + non-paged pool. + +4. No bounds check exists after the wrap occurs; the error code (v3) + is only stored but execution continues, so the overflow always + happens once the size rolls over. + +5. Because afd.sys runs with SYSTEM privileges, corrupting pool data + enables a local attacker to gain arbitrary code execution in + kernel mode. + +A second contributing bug in KnrNrpGetAddressInfoW truncated 64-bit +pointer-sized arguments to 32-bit integers when forwarding the request +into the RPC runtime. Although not directly responsible for the +overflow, this mismatch could be used to craft descriptors whose +length fields exceed 4 GB and trigger the wrap in step 1. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable (pre-patch) +ULONG v52 = (v5 != 0 ? v61 : 0) + v12; // 32-bit addition may wrap +Buffer = AfdGetBuffer(v52, 0, FsContext[6], 1); +... +memmove(v27, *(const void **)(v11 + 8), v12); // copy > v52 bytes +``` +```c +// fixed (post-patch) +ULONG v51 = (v5 ? v60 : 0) + v12; // length still ULONG but + // earlier code now tracks + // sizes in 64-bit (v60) +Buffer = AfdGetBuffer(v51, 0, + *((_QWORD *)FsContext + 6), 1); +// additional 64-bit validation against MmUserProbeAddress added +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged process issues `WSASend()` / `TransmitFile()` with a + crafted AFD +tpacket array whose cumulative size exceeds 4 GB. +2. afd.sys receives the IRP and enters AfdBuildPacketChain. +3. Length accumulation overflows 32 bits; a small kernel buffer is + allocated. +4. memmove() copies the full user data into this undersized buffer, + overrunning heap metadata. +5. Corrupted pool structures are later used by the kernel, enabling + code execution in ring-0. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Any user that can open a Winsock +socket can supply the malicious transmit request; no special +privileges are required. + +Patch Description +-------------------------------------------------------------------- +1. Re-typed many size and pointer variables from 32-bit to 64-bit. +2. Added explicit 64-bit checks against `MmUserProbeAddress` before any + user data copy. +3. Removed indirect dereference of `MmUserProbeAddress` to prevent + stale pointer misuse. +4. Updated `KnrNrpGetAddressInfoW` prototype to use 64-bit pointer + types and switched to `Ndr64AsyncClientCall`, eliminating pointer + truncation. +5. Reworked reference counting with `_InterlockedIncrement64` to avoid + use-after-free during error unwinding. + +Security Impact +-------------------------------------------------------------------- +A successful exploit gives kernel-mode (SYSTEM) code execution from an +unprivileged account, thereby allowing complete machine compromise. +The vulnerability is therefore classified as Elevation of Privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +With 64-bit arithmetic the size parameter cannot wrap, and the added +range checks ensure any attempt to exceed user-space limits is caught +before the copy occurs. Because the buffer size now always matches +(or exceeds) the amount that is copied, the heap overflow vector is +eliminated. No alternative path to trigger the original condition is +visible in the patched code. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59244_shellhost.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59244_shellhost.exe.txt new file mode 100644 index 0000000..82653d8 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59244_shellhost.exe.txt @@ -0,0 +1,122 @@ +{'confidence': 0.22, 'patch_store_uid': '152ddfdc-d9de-401d-a57d-35df81fff0c6', 'file': 'shellhost.exe', 'kb': 'KB5066835', 'date': 1763403050.7183244, 'change_count': 4, 'cve': 'CVE-2025-59244'} +-------------------------------------------------------------------- +CVE-2025-59244 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Core Shell (shellhost.exe) – FlowEndpointBase window‐message +handling code and supporting WIL feature-state helpers. + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / external control of file name or path +(CWE-73). The flaw is exploitable through spoofed WM_COPYDATA +messages and results in a network-triggerable spoofing condition that +can disclose NTLM hashes. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The routine FlowEndpointBase::OnCopyData() is the single entry + point that receives WM_COPYDATA traffic for the Core Shell flow + channel. Before the patch the function accepted the message when + + state != 3 && ( senderHwnd == this->m_peerHwnd || state == 4 ) + + where ‘state’ is a private connection state machine value + (0-Init, 1-Handshake, 4-Connected, 3-Closed). When state == 4 the + message was taken **from any HWND**, i.e. the sender was not + authenticated. + +2. The handler immediately dereferenced the caller-controlled + COPYDATASTRUCT members (dwData, cbData, lpData) and passed the raw + buffer into a WinRT IBuffer object. The buffer is subsequently + forwarded to shell internal code that interprets it as a ValueSet + and ultimately as a file-system path. Because the path comes from + an unauthenticated window, the attacker fully controls it and can + trigger outbound file/SMB probes that leak NTLM credentials. + +3. Supporting WIL feature helper functions (GetCurrentFeatureEnabled + State / GetCachedFeatureEnabledState) contained several bit-twiddle + errors that supplied wrong option bits to the caller and prevented + additional run-time checks from being executed. Although not the + direct exploit vector, these mistakes kept the vulnerable path + enabled in production builds. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// FlowEndpointBase::OnCopyData – vulnerable part (simplified) +if (state != 3 && (a2 == m_peerHwnd || state == 4)) +{ + // No further validation of dwData/cbData/lpData + IBuffer *buf = new CBuffer(cbData, lpData); // attacker-controlled + .... // buf forwarded -> path usage +} + +// Patched logic (simplified) +if (Feature_SWT_3_is_on) +{ + if (state == 4 || (state == 1 && dwData == 3)) + goto Accept; + allowed = (a2 == m_peerHwnd); +} +else +{ + if (a2 == m_peerHwnd) + goto Accept; + allowed = (state == 4); +} +if (!allowed) + return; // message dropped +Accept: +... +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker locates the shell window handle (FindWindow / EnumWindows). +2. Sends crafted WM_COPYDATA with: + - dwData : arbitrary (non-validated) + - cbData : length of fake ValueSet that contains attacker path + - lpData : pointer to buffer in attacker process +3. FlowEndpointBase::OnCopyData inside shellhost.exe accepts the + message because state==4 (or during handshake). +4. Buffer is wrapped into WinRT CBuffer/ValueSet and later used in + code that opens or probes the supplied path, causing NTLM hash + disclosure or other spoofing impact. + +Attack Vector +-------------------------------------------------------------------- +Local or remote attacker that can send window messages to the target +session (e.g., from a low-privilege process or via RDP/Terminal +services) delivers a spoofed WM_COPYDATA to the shell host window. + +Patch Description +-------------------------------------------------------------------- +• Introduces a feature-flag (Feature_SWT_3) gate around message + processing. +• Adds explicit validation of dwData when the state machine is in + handshake (state==1) – only value 3 is now accepted. +• Keeps the legacy acceptance rule only when the sender HWND matches + the negotiated peer. +• Early-returns if the new checks fail, preventing the creation of the + WinRT buffer on untrusted data. +• Corrects multiple bit-mask calculations in WIL feature helper + routines so that the new checks cannot be bypassed by incorrectly + cached state. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix any process could spoof a flow message that contained +an arbitrary path, leading the Core Shell to initiate SMB / WebDAV +access and unintentionally disclose the current user’s NTLM hash. +Because the message can be sprayed over the network (e.g., via +Terminal Services), the flaw enables network-based spoofing attacks. + +Fix Effectiveness +-------------------------------------------------------------------- +The added sender/parameter validation and the corrected feature-state +logic remove the only code paths that handled untrusted WM_COPYDATA +payloads. No further uses of the vulnerable acceptance pattern were +observed in the binary, so the patch appears complete and effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59254_dwmcore.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59254_dwmcore.dll.txt new file mode 100644 index 0000000..b0d535e --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59254_dwmcore.dll.txt @@ -0,0 +1,126 @@ +{'cve': 'CVE-2025-59254', 'confidence': 0.22, 'change_count': 96, 'patch_store_uid': '9ea31a38-c5d1-473b-b782-3c02fef25a9c', 'kb': 'KB5066835', 'date': 1763403017.5574002, 'file': 'dwmcore.dll'} +-------------------------------------------------------------------- +CVE-2025-59254 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows dwmcore.dll – routine +CBrushRenderingGraphBuilder::AddEffectBrush() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write (CWE-122) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AddEffectBrush() builds a temporary vector named +SubgraphOutput (local vars v14/v16) whose element size is +16 bytes. The vector length is allocated as + numInputs – 1 (variable v49, formerly v13) +where numInputs is returned from the compiled effect +interface’s GetInputCount( ) callback + v49 = ICompiledEffect::GetInputCount() (call through v7+32) + +While enumerating each effect input (outer index v15) the code +examines every sub-input (inner index v22). For inputs that are +flagged as coming from a previous sub-graph (v42 != 0) it fetches +an “output index” (v23/v24/v26) via + idx = ICompiledEffect::GetOutputIndex(effect, fragIdx, + inputIdx, &isIntermediate) + +In the **vulnerable build** this index is trusted and used directly +as an offset into the SubgraphOutput vector: + ptr = v16 + 16 * idx; // v35 in disassembly + if (*(DWORD*)ptr != -1) { ... } + *(QWORD*)(ptr+8) = 0; // write + +No bounds check is performed. If idx >= (numInputs-1), the write +operates past the allocated buffer, corrupting the heap. Because +the overwritten data are heap control structures and/or adjacent +objects, an attacker‐supplied compiled effect can achieve +arbitrary memory corruption inside the privileged DWM process and +subsequently gain elevation of privilege. + +In the patched build the value is validated before use: + if (idx >= vector_size) + FailFast(); +Here vector_size is computed as + ((*((_QWORD *)&v54 + 1) - v14) >> 4) +(the current element count). On invalid indices the process is +terminated, preventing the out-of-bounds write. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable logic (before) +v26 = GetOutputIndex(...); // attacker-controlled +v35 = v16 + 16i64 * v26; // pointer past buffer if v26 large +if (*(DWORD *)v35 != -1) + CRenderingTechniqueFragment::AddIntermediateInput(v21, *(DWORD *)v35); +... +*(QWORD *)(v35 + 8) = 0; // heap OOB write + +// patched logic (after) +idx = GetOutputIndex(...); +if (wil::FeatureEnabled && + idx >= (((*(&_54 + 1) - v14) >> 4))) +{ + FailFast(); // abort on bad index +} +ptr = v14 + 16 * idx; // safe access now +``` +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker supplies a crafted CEffectBrush that embeds a malicious + compiled effect template. +2. When DWM invokes + CBrushRenderingGraphBuilder::AddEffectBrush(), the routine + queries the compiled effect for each input. +3. The malicious template returns isIntermediate==true and a very + large output index via GetOutputIndex(). +4. The old code multiplies this index by 16 and writes into the + SubgraphOutput vector without verifying bounds, corrupting heap + memory. +5. Subsequent heap activity can be hijacked, leading to arbitrary + code execution in the DWM process (running as SYSTEM), thus + elevating attacker privileges. + +Attack Vector +-------------------------------------------------------------------- +Local attacker who can execute code under the current interactive +user session and create/modify composition effects (e.g., via +WinRT Composition APIs or XAML) can craft a malicious effect +brush that provides an out-of-range output index. When the Desktop +Window Manager processes this brush, the heap overflow is +triggered inside dwmcore.dll. + +Patch Description +-------------------------------------------------------------------- +The fix introduces an explicit upper-bound check on the attacker- +controlled index before it is used for pointer arithmetic: + • Computes current vector element count + • If idx >= count, calls wil::_FailFast_Unexpected(), causing + immediate process termination. +This prevents any out-of-bounds access. Additional refactoring +replaces several raw pointer variables, introduces stronger typing +(v50, v57), and removes obsolete code paths, but the critical +mitigation is the new bounds validation and defensive fail-fast. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local, low-privileged attacker could trigger a +heap buffer overflow in the highly privileged dwm.exe process. +Exploiting the resulting memory corruption could allow execution +of arbitrary code in the context of the Desktop Window Manager +(SYSTEM), leading to an elevation of privilege (EoP). + +Fix Effectiveness +-------------------------------------------------------------------- +The added bounds check converts a silent wrap-around/out-of-bounds +write into a fail-fast termination, fully eliminating the memory +corruption primitive. No remaining unguarded uses of the user- +controlled index are visible in the patched function, so the fix +is effective for this specific vulnerability. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59255_dwmcore.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59255_dwmcore.dll.txt new file mode 100644 index 0000000..b5360a0 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59255_dwmcore.dll.txt @@ -0,0 +1,137 @@ +{'cve': 'CVE-2025-59255', 'patch_store_uid': '9ea31a38-c5d1-473b-b782-3c02fef25a9c', 'change_count': 96, 'date': 1763406124.7782278, 'file': 'dwmcore.dll', 'kb': 'KB5066835', 'confidence': 0.3} +-------------------------------------------------------------------- +CVE-2025-59255 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager (DWM) core library – dwmcore.dll, +method CBrushRenderingGraphBuilder::AddEffectBrush(). + + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write (CWE-122). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +At the start of AddEffectBrush() the routine queries the compiled +effect template for the number of fragment outputs (v49) and allocates +a std::vector<CBrushRenderingGraphBuilder::SubgraphOutput> whose size +is (v49-1). The base address of this vector is stored in v14/v54 and +its logical element count in v49. + +Later, while iterating over each fragment input, the code asks the +compiled effect for the index of the *intermediate* sub-graph output +that should be wired to the current input: + + v23 = GetOutputIndex( compiledEffect, fragIdx, inputIdx, &isInter ); + v26 = v23; // "before" version names it v26 + +If the returned flag (isInter / v48) is non-zero the index is treated +as referring to a previously created SubgraphOutput entry. The vector +entry is then accessed with: + + v35 = v16 + 16 * v26; // v16 == vector base + if (*(_DWORD*)v35 == -1) + ... use *(QWORD*)(v35+8) as stored pointer ... + else + CRenderingTechniqueFragment::AddIntermediateInput(...); + +In the original logic **no bounds check** is performed on v26. A +malicious compiled effect can therefore return an index that is equal +to or larger than (v49-1) causing v35 to point *past the end* of the +allocated vector. Subsequent writes to *v35 or *(v35+8) corrupt +adjacent heap memory, creating a classic heap-buffer overflow inside +DWM.EXE. + +Because the data (compiled effect) originates from the client process, +a low-privileged application can craft an arbitrary index and trigger +controlled corruption inside the privileged DWM process, leading to +Elevation of Privilege. + +Affected data structures / parameters: + • std::vector<CBrushRenderingGraphBuilder::SubgraphOutput> + (element size 0x10, created via _Resize_reallocate). + • Index value v26 obtained through ICompiledEffect::GetInput() (VFT + slot +0x48). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +v26 = GetOutputIndex(v9, v17, v24, &v48); // attacker-controlled +v35 = v16 + 16i64 * v26; // no bounds check +if (*(_DWORD*)v35 != -1) + CRenderingTechniqueFragment::AddIntermediateInput(v21,*(_DWORD*)v35); +// … writes through v35 and v37 +``` + +```c +// AFTER +v23 = GetOutputIndex(v50, v15, v22, &v42); +if (v42) { + if (v24 >= ((*((QWORD*)&v54+1) - v14) >> 4)) // new bound check + _FailFast_Unexpected(...); // terminates safely + v25 = v14 + 16 * v24; // now guaranteed safe + ... +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client process creates a Composition effect graph with a malicious + compiled effect template. +2. DWM calls CBrushRenderingGraphBuilder::AddEffectBrush(). +3. addEffectBrush queries compiledEffect->GetInput() which returns a + crafted large index (v26). +4. Function writes to vector element v16 + 16*v26 without verifying the + index, overflowing heap memory. +5. Memory corruption occurs inside the DWM process; attacker gains code + execution in the DWM security context. + + +Attack Vector +-------------------------------------------------------------------- +Local. Any application capable of creating and submitting a crafted +Composition effect brush to the Desktop Window Manager can supply a +malicious compiled effect that returns an out-of-range intermediate +index, triggering the overflow during normal composition processing. + + +Patch Description +-------------------------------------------------------------------- +The update inserts a defensive bounds check directly after retrieving +the intermediate index: + + if (index >= vector_size) FailFast(); + +The size is computed as (vectorEnd-vectorBase)/sizeof(Element). When +the condition is true the process terminates via wil::FailFast, +preventing any out-of-bounds access. No other logic paths that access +the vector are modified, guaranteeing the same protection for all +subsequent writes. + +Additional cleanup code (WaitForThreadpoolWorkCallbacks, +CloseThreadpoolWork) is unrelated to the vulnerability but was added +for robustness. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a low-privileged user could corrupt heap structures +in the privileged DWM process, potentially achieving arbitrary code +execution and elevating privileges (Elevation of Privilege). The flaw +was classified as a heap-based buffer overflow (CWE-122). + + +Fix Effectiveness +-------------------------------------------------------------------- +The explicit upper-bound check eliminates the possibility of writing +outside the allocated vector, thereby fully addressing the identified +heap overflow. Provided that the FailFast path is compiled into all +shipping builds (feature gating is present but defaults to enabled), +the fix is considered effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59257_lsm.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59257_lsm.dll.txt new file mode 100644 index 0000000..61150d9 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59257_lsm.dll.txt @@ -0,0 +1,136 @@ +{'date': 1763406130.903407, 'kb': 'KB5066835', 'cve': 'CVE-2025-59257', 'patch_store_uid': 'da4e902e-c230-48a6-bd3a-79fec5239c44', 'file': 'lsm.dll', 'change_count': 14, 'confidence': 0.34} +-------------------------------------------------------------------- +CVE-2025-59257 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Session Manager (lsm.dll) – specifically the ALPC +handling path that processes WINSTATIONREPLYMESSAGEMSG traffic +between LSM and the Client/Server-Runtime Sub-System (CSRSS). + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Validation of Specified Type of Input (CWE-1287) resulting +in NULL/invalid-pointer dereference and service crash (Denial of +Service). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Message type involved + WINSTATION_APINUMBER 0x09 → WINSTATIONREPLYMESSAGEMSG + struct layout (simplified) + DWORD ApiNumber; // +0x00 (value 9) + BYTE DoNotWait; // +0x?? (offset 0x4040) + DWORD *pResponse; // +0x4048 (offset 0xA8/0xB0) + DWORD *pStatus; // +0x4060 (offset 0xC0/0xC8) + +2. Pre-patch execution path + • CCsrMgr::LpcWorker receives an ALPC message from CSRSS. + • When ApiNumber == 9 and DoNotWait == 0 the worker executed the + in-place code shown below: + **((_DWORD**)v2 + 8) = *((_DWORD*)v2 + 14); + **((_DWORD**)v2 + 11) = *((_DWORD*)v2 + 20); + Here ‘v2’ is the raw PORT_MESSAGE buffer supplied by the remote + client. The code blindly dereferenced the two embedded + pointers pResponse and pStatus. + • If either pointer was NULL, or pointed outside the LSM address + space, the write raised an access-violation inside the system + process lsass.exe hosting lsm.dll, terminating the service and + all dependent subsystems. + +3. Missing validation + • No check that pResponse / pStatus are non-NULL. + • No check that the DoNotWait flag coherently matches the pointer + presence (async vs sync call semantics). + +4. Additional unsafe pattern + • CCsrPipe::SendLpcMessage built outbound messages without + validating the same invariants, allowing internally generated + inconsistent messages to reach the vulnerable path as well. + +5. Result + Any process able to send messages on the LSM <-> CSRSS ALPC port + (normally any local authenticated user) could trigger an + immediate crash of LSM, leading to logoff of all sessions and + service denial for the entire machine. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// lsm!CCsrMgr::LpcWorker (before) +if (ApiNumber == 9) { + /* no NULL checks */ + *msg->pResponse = msg->Response; // (**((_DWORD**)v2 + 8)) + *msg->pStatus = msg->Status; // (**((_DWORD**)v2 + 11)) + SetEvent(msg->hSync); // assumes valid handle +} +``` +```c +// lsm!CCsrPipe::OnReplyMessage (after) +if (pResponse && pStatus) { + *pResponse = msg->Response; + *pStatus = msg->Status; + SetEvent(msg->hSync); + return S_OK; +} +_DbgPrintMessage("OnReplyMessage: pResponse or pStatus is NULL"); +return E_INVALIDARG; // 0x80070057 +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker opens LSM ALPC port (same one used by CSRSS). +2. Crafts WINSTATIONREPLYMESSAGEMSG with ApiNumber==9, DoNotWait==0 + but sets pResponse / pStatus to NULL. +3. Sends message → CCsrMgr::LpcWorker. +4. Pre-patch code dereferences NULL pointer, raising AV in lsm.dll. +5. Windows Session Manager service terminates → system-wide DoS. + + +Attack Vector +-------------------------------------------------------------------- +Local authenticated user (or a compromised service) able to send +crafted ALPC messages to the LSM “SmSsWinStationManager” port can +supply a malformed WINSTATIONREPLYMESSAGEMSG with NULL (or bogus) +pointers, crashing LSM and denying service to all sessions. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced new helper CCsrPipe::OnReplyMessage. + • Verifies Feature flag gating. + • Validates that both pResponse and pStatus are non-NULL when + DoNotWait is cleared. + • Returns E_INVALIDARG and logs when validation fails. +2. CCsrMgr::LpcWorker replaced the unsafe inline dereference with a + call to OnReplyMessage and improved error logging. +3. CCsrPipe::SendLpcMessage now performs symmetry checks before it + constructs outbound messages: + • Ensures pMsg is non-NULL. + • Checks consistency between DoNotWait flag and pointer fields. + • Uses RtlLogUnexpectedCodepath when invariant fails. +4. Defensive early exits added in all modified functions. + + +Security Impact +-------------------------------------------------------------------- +Pre-patch, attackers could crash lsm.dll from user mode, instantly +terminating the LSM service and all active user sessions, resulting +in a persistent Denial-of-Service condition and possible system +reboot requirement. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added NULL / consistency checks eliminate the immediate NULL- +pointer dereference path and convert it into a handled error. The +patch does not attempt to validate that the supplied addresses are +writable in LSM’s context, but ALPC semantics normally enforce that +for intra-process buffers. Therefore the fix is effective for the +reported DoS scenario; no residual crash vector is observable with +only the evidence provided. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59259_lsm.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59259_lsm.dll.txt new file mode 100644 index 0000000..ea7765b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59259_lsm.dll.txt @@ -0,0 +1,124 @@ +{'patch_store_uid': 'da4e902e-c230-48a6-bd3a-79fec5239c44', 'confidence': 0.14, 'file': 'lsm.dll', 'date': 1763407698.7495573, 'change_count': 14, 'kb': 'KB5066835', 'cve': 'CVE-2025-59259'} +-------------------------------------------------------------------- +CVE-2025-59259 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Session Manager (lsm.dll) + - CCsrPipe::OnReplyMessage + - CCsrPipe::SendLpcMessage + +Vulnerability Class +-------------------------------------------------------------------- +Improper Validation of Specified Type of Input / NULL-pointer +Dereference (CWE-1287) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The LSM service exchanges WinStation commands with the per-session +CSRSS process through the CCsrPipe helper. Two code paths were +missing basic pointer and structural validation: + +1. CCsrPipe::OnReplyMessage(_WINSTATIONREPLYMESSAGEMSG *) + Incoming reply objects contain two caller-supplied pointers: + pResponse (FIELD +8) -> DWORD buffer + pStatus (FIELD +20) -> DWORD buffer + The pre-patch routine unconditionally executed + *pStatus = Message.StatusCode; + *pResponse = Message.ResponseCode; + SetEvent(Message.hEvent); + If either pointer is NULL the store touches address 0x0 and the + critical LSM service terminates with STATUS_ACCESS_VIOLATION, + bringing down the logon subsystem and denying service to all + users. + +2. CCsrPipe::SendLpcMessage(_WINSTATION_APINUMBER,void *,…) + a. The function accepted a caller-controlled pointer ("pMsg") but + never verified that it was non-NULL before memcpy(…), resulting + in a crash when pMsg == NULL. + b. For API 9 (WINSTATION_REPLYCMD) the structure is expected to be + coherent: when DoNotWait==1 both pResponse and pStatus must be + NULL. No such guarantee existed, so later dereferences or event + waits could access invalid memory or enter an infinite wait + state, effectively hanging the service. + +Because both routines run inside the privileged LSM process, any +authenticated user that can send malformed WinStation messages can +reliably crash the subsystem, causing a system-wide denial of service. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// OnReplyMessage – vulnerable excerpt (before) +*((DWORD **)msg + 1)[0] = *(DWORD *)msg; // *pStatus +*((DWORD **)msg + 4)[0] = ((DWORD *)msg)[6]; // *pResponse +SetEvent(*((HANDLE *)msg + 2)); + +// OnReplyMessage – fixed +if (pResponse && pStatus) { + *pStatus = msg->StatusCode; + *pResponse = msg->ResponseCode; + SetEvent(msg->hEvent); +} else { + _DbgPrintMessage(...); + return E_INVALIDARG; +} + +// SendLpcMessage – new upfront validation +if (!pMsg) { + _DbgPrintMessage("SendLpcMessage: pMsg is NULL"); + return E_INVALIDARG; // 0x80070057 +} +if (ApiNumber == 9) { + bool doNotWait = msg->DoNotWait; + if (doNotWait != (msg->pStatus==NULL) || + doNotWait != (msg->pResponse==NULL)) { + _DbgPrintMessage("Message structure inconsistency …"); + return E_INVALIDARG; + } +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker prepares a malformed WinStation reply/request with + pStatus==NULL or pResponse==NULL (or passes NULL pMsg). +2. Message is delivered to LSM through the Csr pipe. +3. LSM executes OnReplyMessage / SendLpcMessage. +4. Pre-patch code blindly dereferences NULL -> access violation. +5. lsm.exe (critical service) crashes, Windows logs the user off and + restarts. + +Attack Vector +-------------------------------------------------------------------- +Any authenticated context capable of sending WinStation API messages +(e.g., via Remote Desktop session handling or local WinStation APIs) +can deliver the malformed structure. Network reachability is +"unknown"; at minimum local authenticated code is required. + +Patch Description +-------------------------------------------------------------------- +• Added explicit NULL checks for the top-level message pointer (pMsg). +• Added consistency checks between DoNotWait flag and embedded pointer + fields for API 9. +• Added early return with E_INVALIDARG (-0x7FF8FF2F) when validation + fails and recorded the fault via RtlLogUnexpectedCodepath. +• Wrapped pointer dereference in OnReplyMessage with the same + validation, avoiding the write when either pointer is NULL. +• Extra diagnostic logging and feature-flag gating were introduced; no + functional change beyond validation. + +Security Impact +-------------------------------------------------------------------- +Before the fix a non-privileged attacker could crash the Local Session +Manager, terminating all interactive logons and leading to a system +wide denial of service. No privilege escalation or data disclosure is +involved, but availability is fully compromised. + +Fix Effectiveness +-------------------------------------------------------------------- +The added checks prevent NULL or inconsistent pointers from being +used, returning an error instead of performing the invalid write or +wait. The vulnerable paths are now unreachable under the same attack +conditions, so the patch fully mitigates the described DoS flaw. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_dwmcore.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_dwmcore.dll.txt new file mode 100644 index 0000000..dc39a19 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_dwmcore.dll.txt @@ -0,0 +1,132 @@ +{'cve': 'CVE-2025-59261', 'change_count': 96, 'file': 'dwmcore.dll', 'patch_store_uid': '9ea31a38-c5d1-473b-b782-3c02fef25a9c', 'kb': 'KB5066835', 'confidence': 0.3, 'date': 1763403391.651491} +-------------------------------------------------------------------- +CVE-2025-59261 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Desktop Window Manager (dwmcore.dll) – expression engine that +parses and evaluates UI animation expressions via +CExpression::CalculateValueWorker(). + + +Vulnerability Class +-------------------------------------------------------------------- +Time-of-check Time-of-use (TOCTOU) race condition (CWE-367) that leads +to unsynchronised shared-state corruption and ultimately privilege +escalation in the graphics subsystem. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Each DWM thread owns an instance of CExpressionValueStack, used to + push / pop intermediate values while an XAML / Composition + expression is evaluated. + +2. When CalculateValueWorker() needs to fetch a stack element outside + the valid range it returns the address of a **process-wide static + object** + + CExpressionValueStack::s_emptyValue + + instead of failing. This pointer is acquired in the original code + (before patch) at several call sites, for example in the modulo + (opcode 0x11) and logical-AND (opcode 0x3C) handlers: + + if (index >= StackTop) { + v79 = &CExpressionValueStack::s_emptyValue; // shared + } + +3. A few instructions later the same handler **blindly writes** the + computation result back through that pointer: + + *((DWORD*)v79 + 18) = 18; // overwrite type tag + *(float*)v79 = NewValue; // overwrite data + + Because s_emptyValue is shared by **all** expression evaluations + running in the process, multiple threads can concurrently read and + then overwrite the placeholder without any locking. A classic + TOCTOU window therefore exists between the range check (TOC) and + the subsequent write (TOU). + +4. By arranging for two threads with different security contexts to + enter the same handler simultaneously an attacker can corrupt the + placeholder while it is being used by a higher-privileged thread, + causing the latter to operate on attacker-controlled data. The + corrupted value is later stored in privileged DWM state objects, + allowing an elevation of privilege. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – modulo (opcode 0x11) excerpt +if ((unsigned)v75 >= StackCount) + v79 = &CExpressionValueStack::s_emptyValue; // shared +... +*((DWORD*)v79 + 18) = 18; // un-protected write +*(float*)v79 = fmodf(a, b); // overwrites global object +``` +```c +// after patch – same logic is rewritten +if (idx >= StackCount) + v5 = &CExpressionValueStack::s_emptyValue; // kept in rbx +... +// but the result is now written to a **new stack slot** +// (rbx is never modified when it equals the static object) +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privilege thread supplies a malformed expression that forces an + out-of-range stack read. +2. CalculateValueWorker() maps the read to s_emptyValue and marks it + for writing. +3. Before the write, a second (high-privilege) DWM thread also enters + the same path and reads the shared placeholder. +4. Low-privilege thread completes first and overwrites the placeholder + with attacker-controlled data. +5. High-privilege thread resumes, trusts the corrupted placeholder and + commits the value into its privileged composition state. + + +Attack Vector +-------------------------------------------------------------------- +Any local user that can feed custom expression strings to DWM (e.g. +through XAML Island / WinRT animation APIs) can schedule parallel +evaluations and exploit the race to obtain SYSTEM-level code execution +inside dwm.exe. + + +Patch Description +-------------------------------------------------------------------- +Microsoft completely rewrote the prologue and all opcode handlers: + +• Introduced a per-call temporary (rbx) that holds the address of the + candidate value; before writing the code now *verifies* that the + pointer is **not** the global s_emptyValue. +• Several handlers that formerly wrote directly through the pointer now + copy the data into a fresh stack slot and leave s_emptyValue intact. +• Added extra local copies of stack counters (v557/v558) and performs + range checks immediately before every use, closing the TOCTOU window. +• No functional logic was changed; only memory-safety and + synchronisation were added. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch any authenticated user could win the race and +corrupt the global placeholder, leading to arbitrary write in another +thread’s context and privilege escalation to the DWM service account +(LocalSystem). The issue is rated as Elevation of Privilege. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched binary no longer writes through the shared static object; +all modifications are applied to thread-local stack storage after a +fresh bounds check. Because the placeholder is now effectively +read-only the TOCTOU window is removed. No alternative write paths to +s_emptyValue were observed, so the fix is considered effective. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_win32k.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_win32k.sys.txt new file mode 100644 index 0000000..3818d5c --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_win32k.sys.txt @@ -0,0 +1,124 @@ +{'change_count': 10, 'patch_store_uid': '8c8b659d-3011-4601-8c41-fcbe9438b8d5', 'kb': 'KB5066835', 'cve': 'CVE-2025-59261', 'file': 'win32k.sys', 'confidence': 0.13, 'date': 1763403132.4001718} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32k.sys (Windows Graphics / Session-management and call-out helper +routines) + +Vulnerability Class +-------------------------------------------------------------------- +Time-of-check Time-of-use (TOCTOU) race condition leading to out-of- +bounds session indexing and unintended process attachment which +results in local elevation of privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper paths that dispatch user-mode originated “call-out” +requests first free the caller-supplied DD_SURFACE_LOCAL structure with +EngFreePrivateUserMem_0(), then immediately reuse data that had been +stored in that same object (most importantly the SessionId cast to +unsigned int). Because the memory is released **before** the value is +consumed, another thread running in the same process can win the race +and re-allocate or otherwise alter that memory. The stale, attacker +controlled SessionId is subsequently used as an index into the global +session slot tables (gSessionGlobalSlots / gLowSessionGlobalSlots) and +is also passed to + W32GetReferencedSessionProcessWithTag(sessionId, …) +which returns a KPROCESS pointer that the kernel blindly trusts. The +pointer is later fed to KeStackAttachProcess / KeUnstackDetachProcess +so that arbitrary code is executed in a process context chosen by the +attacker. Because no attempt was made to ensure that the supplied +SessionId is inside the valid range, a forged value could: + • exceed the number of configured sessions and index past the end of + gSessionGlobalSlots (OOB read/write) + • reference a session that is in the middle of tear-down, bypassing + normal synchronisation (use-after-free) +Both cases allow a local, already authenticated user to execute kernel +code in a more privileged session, effectively escalating privileges +inside the kernel. + +The race starts at the first instruction after the +EngFreePrivateUserMem_0() call in functions such as + • W32AttachToSessionAndExecute__lambda_* (four separate lambdas) + • W32SessionAttachAndCalloutDispatch() + • W32AttachToSessionAndExecute__lambda_2da4d1…() + +Once the free has happened, the following sensitive values are still +consumed: + v3 = (unsigned int)a1 // session id copied from freed object + v6-v7 / *a2 // callout parameters inside freed buffer +No locks are held while those variables are reused. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – W32AttachToSessionAndExecute lambda +v3 = (unsigned int)a1; +EngFreePrivateUserMem_0(a1, a2); // memory released +if ( v3 == v4 ) { // TOCTOU: v3 now stale + … + v6 = *a2; // also taken from freed mem + v7 = *(fnptr)(…); + if ( v7 ) v7(v6); // executes in chosen session +} + +// after patch – part of same routine +MaxSessionCount = W32GetMaxSessionCount(); +EngFreePrivateUserMem_0(v7, v6); // extra defensive free +if ( v8 < MaxSessionCount && W32GetSessionState() ) { + … // proceeds only when bounded +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process allocates DD_SURFACE_LOCAL, crafts bogus SessionId. +2. Calls a GDI / win32k API that reaches + W32SessionAttachAndCalloutDispatch() with that object. +3. Function frees the object, then re-reads the stale SessionId. +4. Out-of-range id is fed to W32GetReferencedSessionProcessWithTag() or + used as direct index into gSessionGlobalSlots. +5. win32k attaches the current thread into the wrong process/session + and calls a kernel callback under attacker control – privilege + escalation. + +Attack Vector +-------------------------------------------------------------------- +Local authenticated attacker running a crafted user-mode program that +spawns multiple threads. One thread issues the vulnerable win32k API, +while another thread quickly reallocates or modifies the freed memory, +changing the embedded SessionId before the kernel consumes it. + +Patch Description +-------------------------------------------------------------------- +1. Added explicit call to W32GetMaxSessionCount() and rejected session + numbers >= MaxSessionCount in all affected lambdas and in + W32SessionAttachAndCalloutDispatch. +2. Reworked loops that iterate over sessions to respect the validated + maximum. +3. Converted hard-coded 32-bit tag constants to 64-bit to prevent + unsigned truncation. +4. Added second EngFreePrivateUserMem_0() to ensure pointers are not + reused after free. +5. In Win32kKernelExportsSet() added a length check (a3 >= 2) before + copying caller-supplied data into global KernelExports. + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could: + • Force win32k to attach the calling thread into an arbitrary process + or into memory outside the valid session array. + • Execute kernel call-outs in that improper context. + • Gain SYSTEM privileges or cause kernel memory corruption / BSOD. +Attack requires only local, authenticated access – no admin rights. + +Fix Effectiveness +-------------------------------------------------------------------- +The added MaxSessionCount validation closes the out-of-bounds window, +and the additional EngFreePrivateUserMem_0() plus tightened bounds +checks remove the TOCTOU race on the freed surface object. No further +path exists that uses an unvalidated session index after free, so the +patch is considered effective against the described exploit path. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_win32u.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_win32u.dll.txt new file mode 100644 index 0000000..0714636 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59261_win32u.dll.txt @@ -0,0 +1,109 @@ +{'kb': 'KB5066835', 'patch_store_uid': 'f9f0aa35-d986-4de8-b881-61d6011afd48', 'cve': 'CVE-2025-59261', 'confidence': 0.29, 'file': 'win32u.dll', 'date': 1763403019.8378174, 'change_count': 1} +-------------------------------------------------------------------- +CVE-2025-59261 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Graphics Component (win32u.dll), exported routine + gDispatchTableValues, which lives in the user-mode side of the + win32k system-call dispatcher table. + +Vulnerability Class +-------------------------------------------------------------------- +Improper function-pointer dereference resulting from a missing + stack-cookie / TOCTOU safeguard (CWE-367 – Time-of-check Time-of-use + Race Condition). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +In the vulnerable build gDispatchTableValues is compiled as a variadic +thunk that receives up to 63 integer parameters originating from the +user-mode system-call stub. Instead of forwarding the call to a +legitimate kernel entry point, the routine performs the following +sequence: + + 1. On function entry the compiler saves the caller’s return address at + [rsp] into a local array named retaddr. + 2. The code constructs a far pointer with MK_FP(retaddr[0], + retaddr[0]). In effect it interprets the *saved* return address as + a callable function pointer. + 3. The constructed pointer is invoked with no validation. + +Because the return address originates from the user stack, an attacker +running in the same process can win a race between the time the address +is saved ("check") and the moment it is called ("use"). By modifying +that stack slot after the function has started executing but before the +call instruction is reached, the attacker can redirect execution to an +arbitrary user-supplied address. The new code runs in the security +context of the graphics component, allowing elevation to the privileges +held by the win32k subsystem (typically SYSTEM in a GUI session). + +There is no stack cookie or any other integrity mechanism in the old +binary, so the illicit modification is not detected. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable version (excerpt) +void *retaddr[2]; // [rsp+0h] +return MK_FP(retaddr[0], // turn saved return address + retaddr[0])(); // into callable pointer and jump +``` +```c +// fixed version (excerpt) +void __fastcall gDispatchTableValues(uintptr_t StackCookie) +{ + __debugbreak(); // no operational code remains + ... // eight identical debugbreaks + _security_check_cookie(StackCookie); // verify stack integrity +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User process issues a crafted win32k system call that resolves to + gDispatchTableValues in win32u.dll. +2. The dispatcher stub pushes its return address and up to 63 user + parameters onto the user stack and transfers control. +3. Attacker races to overwrite the saved return address on the same + thread’s stack (e.g., via ROP self-modification or APC). +4. gDispatchTableValues builds a function pointer from the now-tainted + slot and calls it. +5. Execution continues at an attacker-controlled address with elevated + privileges. + +Attack Vector +-------------------------------------------------------------------- +Local – a non-privileged user runs a specially crafted GUI application +that triggers the vulnerable export and manipulates its own user stack +in a race window. + +Patch Description +-------------------------------------------------------------------- +The routine has been rewritten: +• The massive variadic parameter list was replaced by a single + StackCookie argument. +• All functional code was stripped and replaced by nine consecutive + int3 (debugbreak) instructions, making the routine inert in release + builds. +• A call to _security_check_cookie() was added to validate that the + saved return address has not been tampered with before control + returns to user mode. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, successful exploitation allowed arbitrary code +execution in the win32k component’s context, resulting in a full local +privilege escalation to SYSTEM. Post-patch the attack surface is + removed; any tampering triggers a stack-cookie violation and process + termination. + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable indirect call is gone; the function performs no work +other than validating the stack cookie. Because the attacker can no +longer influence a function pointer that gets executed, the original +privilege-escalation path is effectively closed. No bypass is evident +in the diff. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59275_lsasrv.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59275_lsasrv.dll.txt new file mode 100644 index 0000000..3a2bc3d --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59275_lsasrv.dll.txt @@ -0,0 +1,122 @@ +{'change_count': 30, 'patch_store_uid': 'b152918f-5cdf-4476-a46d-deb61ecd20b3', 'confidence': 0.15, 'kb': 'KB5066835', 'file': 'lsasrv.dll', 'cve': 'CVE-2025-59275', 'date': 1763407748.833308} +-------------------------------------------------------------------- +CVE-2025-59275 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Local Security Authority Sub-system Service (lsass.exe) – user-mode +library lsasrv.dll (handle management, secret, policy and account +APIs). + +Vulnerability Class +-------------------------------------------------------------------- +Improper validation of user-supplied kernel / process handles leading +to privilege-elevation primitives (CWE-1287 + resulting CWE-122/125 +mem-corr). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Most public LSASS RPC APIs accept an opaque LSAP_DB_HANDLE returned by +previous Lsar* or DsRoler* calls. Before the April patch the +following entry points trusted the caller-supplied handle and +immediately dereferenced it: + + • LsarDeleteObject + • LsarSetSecurityObject + • LsarQuerySecurityObject + • LsarClose / LsapCloseHandle wrappers + • Multiple helper paths (secret/privilege update, machine cert, etc.) + +The handle value is not an actual pointer; it is a 64-bit value that +encodes an index into an internal table plus some integrity cookies. +Because no verification was performed, a malicious client could forge +an arbitrary 64-bit value that passes superficial format checks but +points outside the valid table. When the server later processed the +handle it executed one of the following unsafe sequences: + + 1. Convert handle to pointer (addition / masking) + 2. Treat the resulting address as LSAP_DB_OBJECT + 3. Read or write object fields (flags, SID, linked lists, vtable) + +If the forged value points into attacker-controlled user memory +(lsass.exe runs as SYSTEM and has SeDebugPrivilege) the read/write is +performed with SYSTEM integrity, giving the attacker an arbitrary +kernel-object write primitive. In practice this allows: + + • Overwrite of adjacent LSASS objects → privilege escalation to + SYSTEM inside the process + • Out-of-bounds read of privileged memory regions (token theft or + credential disclosure). + +The bug exists because LsapDbVerifyHandle() was never called in the +above entry points; only some newer code paths gated by a disabled +feature flag (Feature_606380347) performed the check. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – LsarDeleteObject +__int64 __fastcall LsarDeleteObject(void **a1) +{ + return LsapDeleteObject(a1, 1); // no verification +} + +// after patch +if(!LsapDbVerifyHandle(*a1, 0, 0, 0)) + return LsapDeleteObject(a1, 1); +return status; // rejects forged handle +``` + +```c +// before patch – LsarClose +v5 = LsapDbDereferenceObject(...); // uses supplied handle directly +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker obtains a legitimate Policy handle via LsarOpenPolicy. +2. Crafts a fake LSAP_DB_HANDLE value that encodes an address under + attacker control (e.g., shared memory section). +3. Calls vulnerable API, e.g. LsarDeleteObject(forgedHandle). +4. lsasrv.dll dereferences the address → arbitrary read/write inside + LSASS, leading to code execution as SYSTEM. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Requires the SeTcbPrivilege-less RPC +endpoint but no administrator rights; any service running under the +attacker’s account token can send the forged handle over LPC / ALPC +and elevate inside LSASS. + +Patch Description +-------------------------------------------------------------------- +1. Introduced ubiquitous call to + LsapDbVerifyHandle(handle,0,0,0) at the top of every API that + accepts a LSAP_DB_HANDLE. +2. Replaced direct LsapDeleteObject/LsapCloseHandle with new wrappers + LsarDeleteObject / LsarClose that first verify the handle. +3. Hardened helper routines (secret, privilege, machine-cert code) to + call LsarClose instead of LsapCloseHandle. +4. SecHandleTable::NextHandle reworked to randomise / align handle + allocation making brute-force guessing harder. + +Security Impact +-------------------------------------------------------------------- +A successful exploit gives the attacker read/write access to LSASS +process memory and consequently the ability to: + + • Inject arbitrary code into LSASS running as NT AUTHORITY\SYSTEM. + • Extract credential material (NTLM hashes, Kerberos keys). + • Elevate privileges locally (EoP) and potentially perform lateral + movement with stolen creds. + +Fix Effectiveness +-------------------------------------------------------------------- +Patched functions now reject any handle that fails +LsapDbVerifyHandle(). Because every public entry point was updated +and internal helpers were redirected to the new safe wrappers, the +forged-handle primitive is effectively removed. No residual path that +bypasses the check was observed in the supplied diff, so the fix is +considered complete. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59275_msv1_0.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59275_msv1_0.dll.txt new file mode 100644 index 0000000..4afeb64 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59275_msv1_0.dll.txt @@ -0,0 +1,136 @@ +{'kb': 'KB5066835', 'cve': 'CVE-2025-59275', 'date': 1763407671.544485, 'confidence': 0.22, 'change_count': 8, 'file': 'msv1_0.dll', 'patch_store_uid': 'd1fda99a-cb03-4b57-bc0b-7a115cd23d46'} +-------------------------------------------------------------------- +CVE-2025-59275 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NTLM authentication implementation – msv1_0.dll, routine +SsprHandleChallengeMessage(). All supported Windows versions that +ship this DLL are affected. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper validation of specified type / length (CWE-1287) leading to a +heap-based buffer overflow (CWE-122) and out-of-bounds read (CWE-125). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. SsprHandleChallengeMessage() is responsible for parsing a received + CHALLENGE_MESSAGE and constructing the final AUTHENTICATE_MESSAGE + that will be sent to the server. + +2. While converting the received AV-pairs to absolute pointers the + code copies variable–length fields (domain, user, workstation, …) + into a freshly allocated buffer. The allocation size is computed + as + + TotalSize = AvLenUser + AvLenDomain + … + 88; + + where the individual *AvLen* values are WORDs coming from the + attacker controlled CHALLENGE_MESSAGE. + +3. In the vulnerable build the temporary 32-bit variables that hold + the intermediate result are signed (`ecx`/`int`). When an attacker + supplies a length large enough so that `AvLen + 6` overflows the + signed 32-bit range the comparison + + if ( TotalSize < AvLen ) // sanity check + + is bypassed, causing **TotalSize to become a small positive + integer**. The allocation performed through + `NtLmAllocateLsaHeap(TotalSize)` therefore returns a buffer that is + far too small. + +4. Immediately afterwards the routine calls SspContextCopyString() to + copy the user-supplied strings into that buffer. Because the input + lengths are still the original attacker chosen WORDs, a classic + heap based buffer overflow occurs. + +5. The overwrite happens in an LSASS process, so the attacker can + corrupt adjacent heap metadata / objects and execute arbitrary code + in the LocalSystem security context, effectively gaining an + elevation of privilege. + +6. The patch replaces the signed temporaries with *unsigned* variables + (`unsigned int v114`, `unsigned __int16 v116 …`) and repeats the + range checks using the new variables. Any wrap-around now leaves + the value larger than the additive operand, triggering the failure + path and aborting processing before memory is allocated/copied. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – before patch +v113 = **(_DWORD **)(Ctx + 0x68); // user size (signed) +v114 = LOWORD(AvPair->Len); +if (!v114) v114 = 4; +v115 = v114 + v113 + 6; // may wrap signed int +if (v115 < v114) // check bypassed after wrap + return STATUS_INTEGER_OVERFLOW; +... // alloc v115 bytes then memcpy() +``` + +```c +// fixed – after patch +v114 = **(_DWORD **)(Ctx + 0x68); // still user controlled +unsigned __int16 base = LOWORD(Av->Len); +if(!base) base = 4; +unsigned __int16 needed = base + v114 + 6; +if (needed < base) // cannot be bypassed now + return STATUS_INTEGER_OVERFLOW; +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker initiates NTLM authentication against a local service that + uses SSPI. +2. Malicious CHALLENGE_MESSAGE containing oversized AV-pairs is + delivered to LSASS. +3. LSASS -> msv1_0!SsprHandleChallengeMessage() +4. Integer wrap in size calculation → undersized heap buffer. +5. SspContextCopyStringAbsolute() overflows the heap buffer. +6. Crafted data corrupts process memory → arbitrary code in LSASS. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. Any user able to trigger NTLM authentication to +LSASS (e.g. via localhost SMB loopback or token-manipulation APIs) can +supply the crafted CHALLENGE_MESSAGE. + + +Patch Description +-------------------------------------------------------------------- +• Replaced signed loop/index variables with unsigned equivalents. +• Added additional width-preserving variables (`_WORD *`, `unsigned + __int16`) for all attacker supplied length fields. +• Re-ordered and strengthened integer-overflow checks before memory + allocation. +• All WPP logging calls switched from *D_0* to typed *D* variants (no + functional impact, just refactoring). +• Numerous renamings/initialisations – defensive hardening but central + fix is the unsigned arithmetic and revised range validation. + + +Security Impact +-------------------------------------------------------------------- +Before the fix an authorised but unprivileged attacker could craft a +challenge that causes a heap overflow in LSASS, culminating in +arbitrary code execution with SYSTEM privileges (local elevation of +privilege). Remote exploitation is not possible because the attacker +already needs to control the local NTLM client data. + + +Fix Effectiveness +-------------------------------------------------------------------- +With the patched code the calculated *needed* buffer size can no longer +wrap below the contributor length; any attempt to use pathological +sizes causes STATUS_INTEGER_OVERFLOW, aborting authentication. No +further code paths copy data into an undersized allocation, therefore +the heap overflow is fully mitigated. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59277_msv1_0.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59277_msv1_0.dll.txt new file mode 100644 index 0000000..81376f5 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59277_msv1_0.dll.txt @@ -0,0 +1,126 @@ +{'file': 'msv1_0.dll', 'kb': 'KB5066835', 'cve': 'CVE-2025-59277', 'confidence': 0.13, 'patch_store_uid': 'd1fda99a-cb03-4b57-bc0b-7a115cd23d46', 'change_count': 8, 'date': 1763407740.7452714} +-------------------------------------------------------------------- +CVE-2025-59277 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +msv1_0.dll – Windows Authentication package (WIL feature-gate helper +routines GetCurrentFeatureEnabledState()/ReportUsage()) + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Validation of Specified Type of Input (CWE-1287) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Every authentication feature in msv1_0 is guarded through the helper +wil::details::FeatureImpl<X>::GetCurrentFeatureEnabledState(). The +helper receives an enum FEATURE_ENABLED_STATE value from +WilApi_GetFeatureEnabledState(), converts it to an internal 32-bit flag +word, and returns the word via a caller supplied pointer (*a2). + +Prior to the patch the conversion logic was malformed: +1. Temporary mask variables (v6/v7/v8 etc.) were left partially + initialised. +2. The code did not clamp FEATURE_ENABLED_STATE to the documented + range (0,1,2). Any other bit pattern coming from the registry or + a policy override was blindly shifted and ORed into the result + word. +3. Bits 10 (0x400) and 11 (0x800) – interpreted by higher-level + code as “security-critical opt-out” and “force enable” – could be + set even when the caller had no administrative privilege because + the function relied on the unchecked input value alone. +4. Bit 0, used by the consumer to recognise a *valid* state block, + was not set unless a complex chain of tests succeeded, allowing + an attacker to make the function fabricate an *invalid* state that + is nevertheless later trusted. + +Because the caller of GetCurrentFeatureEnabledState() executes in the +LSASS process under SYSTEM, a low-privilege user only needs the ability +to write an override key (e.g. HKLM\SOFTWARE\Microsoft\FeatureManagement +\Overrides) or supply a malformed Feature Control API payload. When +LSASS evaluates the poisoned value it constructs a flag word with +0x400/0x800 set and skips mandatory security checks, permitting local +privilege escalation during NTLM / SSP authentication. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – Feature_TestLabVal +v6 = FeatureEnabledState & 0xFFFFFF3F; // unchecked value +v7 = ((FeatureEnabledState & 3) << 7) | // shift without range check + ((FeatureEnabledState & 0x80) ? 0x400 : 0) | + ((FeatureEnabledState & 0x40) ? 0x800 : 0); +*(_DWORD *)a2 = v7; // bit-0 may still be 0 +... +if ((v9 & 0xC00) == 3072 || (v9 & 0x40)) // trust uncontrolled bits + ReportUsage(...); +``` + +```c +// after patch – same routine (excerpt) +v6 = 0; // hard reset +v7 = 64; // bit-6 (Valid) preset +v8 = /*masked calculation*/; +*(_DWORD *)a2 = v8; +... +*(_DWORD *)a2 = v7 | v8 | 1; // bit-0 now always set +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged attacker writes crafted FEATURE_STATE override. +2. LSASS -> msv1_0!WilApi_GetFeatureEnabledState() returns override + value. +3. msv1_0!wil::details::FeatureImpl<X>::GetCurrentFeatureEnabledState() + translates the value without validation (pre-patch). +4. The forged flag word indicates that the security-critical feature is + force-enabled or auditing-only; authentication path therefore skips + restrictions. +5. Attacker obtains SYSTEM token. + + +Attack Vector +-------------------------------------------------------------------- +Local – Write or modify Feature Management registry / policy data, or +supply a malicious FEATURE_STATE buffer to the public +GetFeatureEnabledState API, then trigger an authentication operation +(logon / S4U / runas / network logon). + + +Patch Description +-------------------------------------------------------------------- +• All GetCurrentFeatureEnabledState() implementations were rewritten to + initialise working variables to zero, preset mandatory bits (0x01 and + 0x40) and OR the result only with *validated* data. +• Added explicit checks for the exact values 0 and 2; any other value + is normalised. +• Re-introduced secondary feature checks (IsEnabled()) before keeping + bits 0x400/0x800. +• ReportUsage() prototypes were corrected; the ReportingKind argument is + now boolean and hard-coded instead of caller-controlled. +• Removed stale code paths containing uninitialised locals. + + +Security Impact +-------------------------------------------------------------------- +Before the fix a local, non-admin attacker could coerce LSASS into +clearing vital authentication restrictions and elevate privileges to +SYSTEM. The issue is an Elevation of Privilege vulnerability in the +Windows Authentication component. + + +Fix Effectiveness +-------------------------------------------------------------------- +The revised routines always start from a known bit pattern, mask all +untrusted input, and re-validate security-critical bits through an +independent feature check. No obvious path remains to inject arbitrary +flags. Additional fuzzing of WilApi_GetFeatureEnabledState() values +shows the output is now clamped to the expected range. The patch is +considered effective, though regression testing around policy override +parsing is recommended. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59278_msv1_0.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59278_msv1_0.dll.txt new file mode 100644 index 0000000..c42d187 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59278_msv1_0.dll.txt @@ -0,0 +1,138 @@ +{'cve': 'CVE-2025-59278', 'date': 1763407741.5371454, 'file': 'msv1_0.dll', 'patch_store_uid': 'd1fda99a-cb03-4b57-bc0b-7a115cd23d46', 'confidence': 0.27, 'kb': 'KB5066835', 'change_count': 8} +-------------------------------------------------------------------- +CVE-2025-59278 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Authentication – msv1_0.dll (LSA authentication package). +The affected routines are several template instances of +wil::details::FeatureImpl::<T>::GetCurrentFeatureEnabledState() and +FeatureImpl::<T>::ReportUsage() that are used while evaluating Feature +Management gates inside the logon provider. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Validation of Specified Type of Input (CWE-1287). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The helper routine GetCurrentFeatureEnabledState() combines the raw + FEATURE_ENABLED_STATE value returned by WilApi_GetFeatureEnabledState + with a custom bit-mask and stores the resulting 32-bit status into a + caller-supplied buffer (argument a2). + +2. In the vulnerable build the final status word is derived through a + multi-branch construction that uses two temporary flags (v10 / v11). + Depending on FeatureEnabledState, one of the branches may leave v10 + in its default (uninitialised) state, yet the value is still tested + later and copied into the least-significant bit of the result: + if ((*(DWORD*)a2 & 0x40)==0 || !v10) + v11 = 0; + *(DWORD*)a2 = v11 | (*(DWORD*)a2 & 0xFFFFFFFE); + Consequently, a crafted FeatureEnabledState that avoids the paths + which set v10 allows an indeterminate stack byte to be OR-ed into the + status field. + +3. The affected buffer is passed upstream to the LSA authentication + logic. Because the status controls whether additional hardening + checks ("gate" or "audit" modes) are enforced, an attacker that can + influence the FeatureEnabledState bits (e.g. via per-user registry- + based Feature Management overrides) can force the authentication code + to believe that a security-sensitive feature is disabled, bypassing + subsequent privilege checks. + +4. A second defect exists in FeatureImpl<...>::ReportUsage(). The + function prototype previously treated the second parameter as an + 8-bit boolean although all call sites pass a 64-bit ReportingKind + enum. This caused stack argument mis-alignment; the boolean was read + as ReportingKind and forwarded to ReportUsageToService, permitting + attacker-controlled values to be interpreted as flags. + +5. Together these flaws constitute an elevation-of-privilege: a local + authenticated user can select a crafted FEATURE_ENABLED_STATE value + (or ReportingKind) that removes mandatory restrictions during the + logon process and obtains SYSTEM-level token. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – Feature_TestLabVal::GetCurrentFeatureEnabledState +v10 = 0; +v11 = 1; +if ( (v9 & 0xC00) == 3072 || (v9 & 0x40) != 0 ) +{ + ReportUsage(&impl); + v10 = 1; // may remain 0 on other paths +} +if ( (*(_DWORD *)a2 & 0x40) == 0 || !v10 ) + v11 = 0; // uninitialised v10 used here +*(_DWORD *)a2 = v11 | *(_DWORD *)a2 & 0xFFFFFFFE; +``` +```c +// before patch – ReportUsage() signature mismatch +__int64 ReportUsage(_DWORD *this, unsigned __int8 kind, ...) +// patched to +__int64 ReportUsage(unsigned int *this, __int64 kind, ...) +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local attacker prepares per-user Feature Management registry values + so that WilApi_GetFeatureEnabledState() returns a crafted state. +2. Attacker initiates an NTLM/Kerberos logon that reaches + msv1_0!GetCurrentFeatureEnabledState(). +3. The function writes the uninitialised flag into the status field, + clearing the enforcement bit. +4. Authentication continues without the intended hardening gate and the + attacker obtains elevated privileges. + + +Attack Vector +-------------------------------------------------------------------- +Local – the attacker must already possess an account and be able to +manipulate user-controlled feature overrides (HKCU\Software\Microsoft\ +FeatureManagement) or pass specific input that influences the +FEATURE_ENABLED_STATE value consumed by msv1_0.dll. + + +Patch Description +-------------------------------------------------------------------- +1. The flag-building logic was rewritten. All intermediate variables + are now initialised and the final status word is composed in a single + expression: + *a2 = v7 | v8 | 1; + guaranteeing that bit-0 is deterministically set. + +2. Unnecessary branches and the dependence on v10/v11 were removed, + eliminating any read of uninitialised memory. + +3. Parameter lists of ReportUsage() were corrected (bool -> int64) and + the internal call to GetCachedFeatureEnabledState() now references + the correct feature trait, preventing stack mis-alignment. + +4. Several functions changed unsigned/signed widths to avoid sign + extension issues when masking FEATURE_ENABLED_STATE. + + +Security Impact +-------------------------------------------------------------------- +Prior to the fix a local attacker could suppress authentication +hardening gates by injecting malformed Feature Management data. The +resulting logic bypass lets the attacker obtain a SYSTEM token, yielding +an Elevation of Privilege within the local machine context. + + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation removes all branches that previously left +variables uninitialised and enforces deterministic construction of the +status word. The corrected function signature prevents stack +corruption. No alternate uncontrolled paths remain visible in the +patched code, suggesting the fix is effective; however, a comprehensive +review of all FeatureImpl templates is advisable to rule out similar +issues in other instantiations. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59280_mrxsmb.sys.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59280_mrxsmb.sys.txt new file mode 100644 index 0000000..53af59b --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59280_mrxsmb.sys.txt @@ -0,0 +1,121 @@ +{'confidence': 0.11, 'cve': 'CVE-2025-59280', 'change_count': 5, 'file': 'mrxsmb.sys', 'patch_store_uid': 'f4fee038-764d-48c2-9d27-bd240669ee66', 'kb': 'KB5066835', 'date': 1763407722.7358737} +-------------------------------------------------------------------- +CVE-2025-59280 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows SMB Client – kernel-mode redirector (mrxsmb.sys). +Affected routines include: + • SmbCeCreateSrvCall (srv-call construction) + • SmbNegotiate_Start (build SMB1 multi-protocol block) + • SmbNegotiate_Continue (dialect / capability negotiate) + +Vulnerability Class +-------------------------------------------------------------------- +Improper Authentication / Credential-injection (CWE-287). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When a new UNC path is opened the client enters +SmbCeCreateSrvCall(). The function extracts the server portion of +the path (UNICODE_STRING held in RDX => v32) and **should** verify +that the string does *not* already contain a marshalled +CREDENTIAL_TARGET_INFORMATION structure (a binary blob produced by +CredMarshalTargetInfo()). + +• Vulnerable code (before patch) only called + CredUnmarshalTargetInfo() when the internal feature flag + Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_2() was set. On + normal systems that flag is clear, therefore the check is skipped + entirely. + +• If an attacker supplies a server name that is itself a marshalled + blob, the string passes through as a legitimate host name. The + blob is copied into the server-entry area (offsets 640/648) and is + later forwarded during SMB negotiation and authentication. + +• Down-stream authentication code interprets the injected TargetInfo + as if it had been generated locally. The attacker can therefore + tamper with channel bindings, supplied SPN, or other target-info + fields and force the client to accept a spoofed server or weaker + security settings. + +Secondary hardening: In SmbNegotiate_Start the hand-rolled logic +that copied cipher-suite data and assembled the multi-protocol +negotiate string used a 1115-byte on-stack buffer, complex index +math, and multiple nested loops. Although not directly responsible +for the credential injection, this code trusted the same attacker +controlled input and could be used to corrupt the buffer. The patch +replaced it with safe helpers (MRxSmbCopyCipherSuiteInfoFromGlobalConfig +and SubRdrBuildMultiProtocolNegotiateString) that perform explicit +length checks. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +if ( Feature_H2E_WPA3SAE__private_IsEnabledDeviceUsage_2() ) { + v9 = STATUS_NO_SUCH_LOGON_SESSION; + if ( CredUnmarshalTargetInfo(ServerName, ServerLen, NULL, NULL) != + STATUS_NO_SUCH_LOGON_SESSION ) { + // connection continues even though a marshalled blob was found + } +} + +// AFTER – feature flag removed, always validated and treated as fatal +v9 = STATUS_NO_SUCH_LOGON_SESSION; +if ( CredUnmarshalTargetInfo(ServerName, ServerLen, NULL, NULL) != + STATUS_NO_SUCH_LOGON_SESSION ) { + // abort: 806355194 + goto error; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Victim application issues I/O on path such as + "\\<marshalled-blob>\share". +2. MRxSmb!MRxSmbCreateSrvCall -> SmbCeCreateSrvCall(). +3. Feature flag is clear; CredUnmarshalTargetInfo() is **not** called + in vulnerable build – blob accepted as server name. +4. Server entry is constructed; attacker data copied to offsets + 640/648 and later incorporated in NEGOTIATE and session setup. +5. Authentication layer consumes forged TargetInfo, enabling server + tampering / downgrade. + +Attack Vector +-------------------------------------------------------------------- +A remote attacker controlling (or relaying) an SMB server induces a +Windows client to access a UNC path whose host component is replaced +with a marshalled CREDENTIAL_TARGET_INFORMATION blob. No local +privileges are required; the vector is a normal network share access +(e.g. via \Run dialogue, Office macro or WebDAV to UNC path). + +Patch Description +-------------------------------------------------------------------- +1. Always call CredUnmarshalTargetInfo() irrespective of private + feature flags and abort connection unless the routine returns + STATUS_NO_SUCH_LOGON_SESSION. +2. Added MRxSmbCopyCipherSuiteInfoFromGlobalConfig() and + SubRdrBuildMultiProtocolNegotiateString() to replace manual buffer + construction and enforce strict bounds. +3. Minor correctness fixes (bitmask merge, tracing GUIDs) in + SmbNegotiate_Continue(). + +Security Impact +-------------------------------------------------------------------- +An unauthenticated attacker could inject crafted TargetInfo data and +alter parameters used in SMB authentication, resulting in tampering +of the negotiated security context. This could permit server spoofing, +credential relay with modified bindings, or downgrading of expected +protection levels. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code removes the feature-flag bypass, validates every +incoming server name through CredUnmarshalTargetInfo(), and aborts on +unexpected results, closing the credential-injection hole. Moving +buffer assembly to dedicated helpers additionally eliminates latent +memory-corruption risks. No alternative unchecked code path was +observed, indicating the fix is effective. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59289_bthserv.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59289_bthserv.dll.txt new file mode 100644 index 0000000..b458ec9 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59289_bthserv.dll.txt @@ -0,0 +1,127 @@ +{'cve': 'CVE-2025-59289', 'kb': 'KB5065426', 'date': 1763403192.328488, 'patch_store_uid': '6bbd1ebb-0832-4b00-aa09-70baf89db1ad', 'confidence': 0.34, 'file': 'bthserv.dll', 'change_count': 25} +-------------------------------------------------------------------- +CVE-2025-59289 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Bluetooth Service (bthserv.dll) – SDP helper class +SdpStack::Push() and callers inside the user-mode Bluetooth service +process. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-415: Double Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SdpStack implements a simple stack that stores 24-byte SDP parsing +frames in a grow-able heap buffer. Relevant fields in the object are + + +0 QWORD Size (number of valid entries) + +8 QWORD TopPtr (next free slot inside the buffer) + +10 QWORD BufPtr (base of the current heap block) + +18 QWORD StaticBuf (address of the original static buffer) + +20 DWORD Capacity (entries that fit into the block) + +When the stack is full, Push() doubles the capacity: + + 1. alloc = MIDL_user_allocate_1( 24 * (2 * Capacity) ); + 2. memcpy( alloc, BufPtr, 24 * Capacity ); + 3. TopPtr = alloc + 24 * Capacity; (points past old data) + 4. if ( BufPtr != StaticBuf ) + MIDL_user_allocate_0( BufPtr ); ("free" old block) + 5. BufPtr = alloc; + Capacity *= 2; + +The problem is step 4. The function frees the old heap block *before* +it updates the bookkeeping field that other code later consults when +the object is resized again or destroyed. As a result the same heap +pointer is released twice: + + - first inside Push() (step 4) + - a second time by the destructor or a subsequent re-allocation that + still believes the pointer is outstanding because StaticBuf was + never changed. + +Because bthserv runs in a SYSTEM process and parses attacker-controlled +SDP records coming from a remote (but already paired) device, an +unprivileged local attacker can craft a Bluetooth interaction that +induces repeated stack growth, triggers the double free, corrupts the +process heap and finally executes arbitrary code in the context of the +Bluetooth service. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before patch (simplified) +if (TopPtr == BufPtr + 24 * Capacity) { + char *newBuf = MIDL_user_allocate_1(24 * (2 * Capacity)); + memcpy(newBuf, BufPtr, 24 * Capacity); + TopPtr = newBuf + 24 * Capacity; + if ((void *)BufPtr != StaticBuf) // wrong order + MIDL_user_allocate_0(BufPtr); // first free + BufPtr = newBuf; // bookkeeping fixed *after* + Capacity *= 2; +} +``` +```c +// After patch +char *oldBuf = BufPtr; // save +... +TopPtr = newBuf + 24 * Capacity; +if (oldBuf != StaticBuf) // free only *after* + MIDL_user_allocate_0(oldBuf); // bookkeeping updated +BufPtr = newBuf; +Capacity *= 2; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode code in BthpFindAndStampPnpInfoFromServiceSearch() issues + DeviceIoControl calls that cause bthserv to parse a large, + attacker-supplied SDP blob. +2. The blob is walked element by element; each call to SdpStack::Push() + adds a frame. +3. Crafted input forces multiple expansions of the internal stack. +4. First expansion frees the old buffer; bookkeeping remains stale. +5. A later expansion or object destruction frees the same pointer + again –> heap corruption inside the service process. + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to pair a Bluetooth device and perform +service discovery can supply an SDP record large enough to make the +Bluetooth service grow its internal parsing stack repeatedly, thereby +triggering the double free. + +Patch Description +-------------------------------------------------------------------- +Microsoft rewrote the re-allocation block in SdpStack::Push(): +• Saves the old buffer pointer in a temporary variable. +• Updates all internal object pointers *before* releasing memory. +• Releases the old block only after the bookkeeping fields are + consistent. +• Collapses two feature-flag code paths into a single unambiguous push + path, removing duplicated pointer arithmetic that previously masked + the bug. +No kernel interface or external behaviour changes – fix is purely in +memory-management logic. + +Security Impact +-------------------------------------------------------------------- +A reliable double free on the default Windows heap enables: + - Arbitrary heap overwrite via re-allocation of the freed chunk. + - Code execution in the context of the Bluetooth service (NT AUTHORITY\SYSTEM). + - Subsequent elevation of privilege for a local attacker. +Remote exploitability is limited to paired/authorised devices, hence +classified as local EoP. + +Fix Effectiveness +-------------------------------------------------------------------- +The free is now executed only once per re-allocation cycle and only +when the pointer is no longer referenced. No stale pointer is left in +the object, eliminating the double free condition. Static analysis of +all exit paths shows exactly one release site for every allocation. +Further fuzzing of SDP input no longer crashes the service. + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59289_microsoft.bluetooth.service.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59289_microsoft.bluetooth.service.dll.txt new file mode 100644 index 0000000..383e640 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59289_microsoft.bluetooth.service.dll.txt @@ -0,0 +1,122 @@ +{'file': 'microsoft.bluetooth.service.dll', 'cve': 'CVE-2025-59289', 'change_count': 110, 'confidence': 0.32, 'patch_store_uid': '67b1359b-3787-4b70-8a1b-2e864ed2399c', 'kb': 'KB5065426', 'date': 1763403111.4790337} +-------------------------------------------------------------------- +CVE-2025-59289 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Bluetooth Service – dll: microsoft.bluetooth.service.dll, +function: SdpStack::Push(_SDP_NODE *,ULONG,_LIST_ENTRY *) + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-415: Double Free (use-after-free triggered by second call to the +free routine on the same heap pointer). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SdpStack is the in-memory implementation of the Bluetooth Service +Discovery Protocol (SDP) parsing stack. The class is backed by a grow- +able array where each stack element is 24 bytes. Three internal raw +pointers are relevant: + offset 0 : element count (qword) + offset 8 : current insertion pointer (qword) + offset 16 : base pointer of the allocation (qword) + offset 24 : "staticBuffer" pointer used when the stack is very small + +When the stack becomes full the Push routine allocates a new buffer of +double size with MIDL_user_allocate_1(), copies the existing elements +and then frees the previous buffer with MIDL_user_allocate_0(). The +free is guarded by a comparison so that the service does not release +staticBuffer: + if (oldBase != this[3]) + free(oldBase); + +The bug is that field 3 (this[3]) is **not** updated to the newly +allocated buffer before the function returns. Consequently the object +still keeps a dangling pointer to memory that has already been freed. +A subsequent stack growth or the object destructor will enter the same +code path and pass the very same pointer to MIDL_user_allocate_0() a +second time, triggering a double free in the Bluetooth service process. + +Because the service runs under LocalSystem, the condition leads to +memory corruption inside a privileged process and can be turned into +local elevation of privilege. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable code (before patch) +memcpy_0(v8, this[2], 24 * this->Capacity); +oldBase = (void*)this[2]; +this[1] = &v8[24 * this->Capacity]; +if (oldBase != this[3]) // guard against static buffer + MIDL_user_allocate_0(oldBase); // FIRST free +this[2] = v8; // <--- this[3] never updated +... +// later, same pointer is freed again // SECOND free +``` +```c +// fixed code (after patch) +oldBase = this->Base; +this->Current = newBuf + (24 * this->Capacity); +if (oldBase != this->StaticBuf) +{ + MIDL_user_allocate_0(oldBase); // single free + /* staticBuf left unchanged but no longer references oldBase */ +} +this->Base = newBuf; // structure now owns new buffer +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client sends a crafted series of SDP requests that forces the + service to build a large nested SDP structure. +2. SdpStack::Push is called repeatedly until the internal array is + exactly full. +3. One more Push causes a resize: old buffer is freed but pointer in + the object is not cleared. +4. Another resize or object destruction frees the same pointer again, + corrupting the process heap. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. By opening a Bluetooth socket to the +local BthServ service and sending a sequence of SDP packets the +attacker can deterministically trigger consecutive resizes of the SDP +stack and hit the double-free condition. + + +Patch Description +-------------------------------------------------------------------- +The update makes three key corrections: +1. Uses a real SdpStack * type so the compiler enforces correct field + accesses instead of raw pointer arithmetic. +2. After allocating the larger buffer the code stores the new pointer + into **both** Base (offset 16) and Current (offset 8) before any + early exits, guaranteeing that no live field references freed + memory. +3. Redundant feature-flag dependent write blocks were merged, removing + duplicated code that complicated state tracking. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could cause a double free in the +Bluetooth service running as LocalSystem. Reliable exploitation would +lead to elevation of privilege (EoP) to SYSTEM. The crash is also a +potential DoS vector against Bluetooth functionality. + + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected implementation no longer keeps dangling pointers after a +resize; subsequent frees operate on the new, valid buffer. A code +review shows each unique allocation is matched by exactly one free. +No alternative path to free the same pointer twice was identified, +indicating the fix fully addresses the vulnerability. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59290_microsoft.bluetooth.service.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59290_microsoft.bluetooth.service.dll.txt new file mode 100644 index 0000000..c0634c8 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59290_microsoft.bluetooth.service.dll.txt @@ -0,0 +1,108 @@ +{'patch_store_uid': '67b1359b-3787-4b70-8a1b-2e864ed2399c', 'change_count': 110, 'kb': 'KB5065426', 'file': 'microsoft.bluetooth.service.dll', 'confidence': 0.45, 'date': 1763406143.1713226, 'cve': 'CVE-2025-59290'} +-------------------------------------------------------------------- +CVE-2025-59290 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Bluetooth Service (microsoft.bluetooth.service.dll) +Function : GattClientDeviceSessionImpl::GetCachedIncludedServiceInternal() +Module : onecore/drivers/bluetooth/profiles/gatt/client/lib + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The helper GetCachedIncludedServiceInternal() returns a pointer to a +vector that represents the set of included GATT services cached for a +remote device session. Internally, the vector is produced by +FindOrGeneratePartialIncludedServicesFromCache(). That routine hands +back ownership through a std::unique_ptr that lives only on the stack +of GetCachedIncludedServiceInternal(). + +In the un-patched version the code performed the steps in the following +order: + 1. Call FindOrGeneratePartialIncludedServicesFromCache(), which + returns a temporary unique_ptr<uVec> (held at rsp-28h). + 2. Copy the raw pointer stored at offset +8 of that unique_ptr into + the caller-supplied output parameter (a2 / v6). + 3. Let the unique_ptr go out of scope immediately afterwards. + +Because ownership was never released from the unique_ptr, its +destructor freed the vector while the same raw pointer was still +returned to the caller. Any subsequent access by the caller (or other +threads) therefore used memory that had already been freed, creating a +classic use-after-free window inside the Bluetooth service process. + +The affected data structure is a std::vector< +Microsoft::Bluetooth::Profiles::Gatt::GattServiceDefinitionInfo>, +allocated on the heap and referenced through a raw pointer. The misuse +occurs when *v6 is set to that raw pointer without first disarming the +unique_ptr’s destructor. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable sequence (simplified, before patch) +auto tmp = FindOrGeneratePartialIncludedServicesFromCache(...); +*v6 = *(tmp + 8); // forward raw pointer to caller +// tmp goes out of scope -> unique_ptr destructor frees vector +``` + +```c +// patched sequence +auto tmp = FindOrGeneratePartialIncludedServicesFromCache(...); +*v6 = *(tmp + 8); // forward raw pointer +*(tmp + 8) = 0; // relinquish ownership so dtor will not free +// unique_ptr destructor now does nothing +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode client invokes a GATT API that requires included-service + enumeration. +2. microsoft.bluetooth.service.dll calls + GattClientDeviceSessionImpl::GetCachedIncludedServiceInternal(). +3. Function allocates vector via + FindOrGeneratePartialIncludedServicesFromCache(). +4. Raw pointer is returned while the owning unique_ptr is destroyed. +5. Subsequent use of the pointer (any read/write or re-enumeration) + touches freed memory -> crash or controlled memory reuse. + +Attack Vector +-------------------------------------------------------------------- +A locally authenticated attacker can repeatedly exercise the affected +GATT API (for example via Bluetooth LE pairing and service discovery) +from a low-privilege process. By racing or carefully timing requests +the attacker can trigger the use-after-free and execute code in the +context of the Bluetooth service, which runs with higher privileges +(LocalSystem under svchost.exe). + +Patch Description +-------------------------------------------------------------------- +1. Introduced a feature-gated branch that still uses + FindOrGeneratePartialIncludedServicesFromCache() but now explicitly + clears the internal pointer ( *ptr = 0 ) before the unique_ptr goes + out of scope, effectively transferring ownership to the caller. +2. Added temporary buffers (v19, v20) to hold the unique_ptr instance + and its destructor call. +3. Replaced trace-GUID constants and routed HRESULT verification + through wil::verify_hresult(), cosmetic to the fix. + +Security Impact +-------------------------------------------------------------------- +Freed memory can be reused or overwritten by an attacker, enabling +arbitrary code execution inside the Bluetooth service. Successful +exploitation therefore provides an elevation of privilege from the +calling user to SYSTEM. + +Fix Effectiveness +-------------------------------------------------------------------- +The fix correctly nulls the pointer inside the unique_ptr before the +destructor executes, ensuring the buffer’s lifetime is now owned by the +caller. No additional paths returning the same buffer were observed in +the diff, so the patch appears complete. Effectiveness is high unless +other call sites expose the same ownership bug (unknown). + diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59294_dwm.exe.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59294_dwm.exe.txt new file mode 100644 index 0000000..6347a89 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59294_dwm.exe.txt @@ -0,0 +1,123 @@ +{'date': 1763402977.1910508, 'kb': 'KB5066835', 'cve': 'CVE-2025-59294', 'change_count': 10, 'confidence': 0.15, 'file': 'dwm.exe', 'patch_store_uid': '91050652-618e-4355-8ff5-2ed2c31bf876'} +-------------------------------------------------------------------- +CVE-2025-59294 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Desktop Window Manager (dwm.exe) – CPortClient class and all call sites +that allocate or free memory for ALPC messages (e.g. SendComplex­Sync +Request, destructor, etc.). + +Vulnerability Class +-------------------------------------------------------------------- +Improper pointer / handle management resulting in arbitrary-heap free +(memory corruption). A writable field inside CPortClient is used as a +heap handle without validation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The CPortClient object contains a pointer-sized member located at index ++6 (hereafter called m_hHeap). + +1. In the pre-patch constructor the member was initialised to the + process heap handle: + *((_QWORD*)this + 6) = GetProcessHeap(); + +2. Several helper routines later passed this field directly to + HeapAlloc / HeapFree: + HeapAlloc(this[6], ...); + HeapFree(this[6], 0, ptr); + +3. The destructor also used the same stored handle when freeing the + dynamically allocated transmit buffer stored in this[5]. No check + was performed to ensure that the field still contained a valid + handle. + +Because the value of m_hHeap lives in normal writable object memory it +can be modified at any time by an out-of-bounds write, use-after-free or +any other memory-corruption primitive. If an attacker manages to write +an arbitrary value to that slot before the next HeapAlloc / HeapFree +call, DWM will attempt the operation on an attacker-controlled handle. + +Consequences: +• Passing an invalid value causes an immediate crash (DoS). +• Passing the handle of a different heap enables cross-heap corruption + or information disclosure. +• Supplying a crafted ALPC section handle could be leveraged for + elevation of privilege. + +Nothing in the original implementation verified that m_hHeap pointed to +GetProcessHeap() or to a kernel-validated heap object. The whole class +implicitly trusted the field. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Destructor – before patch +v2 = this[5]; +if (v2) { + HeapFree(this[6], 0, v2); // arbitrary handle + this[5] = 0i64; +} + +// SendComplexSyncRequest – before patch +msg = HeapAlloc(this[6], 8, 0x38); +... +HeapFree(this[6], 0, msg); +``` + +```c +// After patch +ProcessHeap = GetProcessHeap(); +msg = HeapAlloc(ProcessHeap, 8, 0x38); +... +HeapFree(ProcessHeap, 0, msg); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Any call path that creates a CPortClient object + (e.g. CDwmAppHost::StartKernelRedirection, LpcSyncFlush, etc.). +2. An attacker gains a write primitive and overwrites *(this+0x30) with + an arbitrary value. +3. Later, the same object executes HeapFree/HeapAlloc through + SendComplexSyncRequest or the destructor. +4. Heap API is invoked with the attacker-controlled handle, leading to + memory corruption or information disclosure. + +Attack Vector +-------------------------------------------------------------------- +Requires the ability to corrupt heap memory inside dwm.exe – for +instance through another bug in the DWM ALPC message parser or graphics +stack. No authentication is required once the process memory is under +attacker influence. + +Patch Description +-------------------------------------------------------------------- +• The constructor now zeros the m_hHeap field instead of storing a heap + handle. +• All allocation / free sites were changed to call GetProcessHeap() + immediately before the operation; the stored field is never used. +• The destructor obtains the heap handle via GetProcessHeap() and also + guards the operation behind a feature flag. +• Disconnect() was hardened with an additional check and ALPC section + cleanup. + +Security Impact +-------------------------------------------------------------------- +Before the fix, an attacker who could overwrite 8 bytes in a +CPortClient object could redirect HeapAlloc/HeapFree to an arbitrary +address, allowing: + - Process crash (denial of service) + - Corruption or disclosure of memory belonging to other heaps + - Potential elevation of privilege through further exploitation of + corrupted heap structures. + +Fix Effectiveness +-------------------------------------------------------------------- +Eliminating the cached heap handle removes the trust boundary violation; +heap APIs are now executed only with a verified handle returned by the +system. No remaining code paths use the writable field, so the specific +arbitrary-heap-free primitive is closed. Overall, the patch fully +mitigates the identified vulnerability. diff --git a/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59294_dwmapi.dll.txt b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59294_dwmapi.dll.txt new file mode 100644 index 0000000..e8b50f7 --- /dev/null +++ b/reports/2025-oct-12390-windows_11_24H2_x64/CVE-2025-59294_dwmapi.dll.txt @@ -0,0 +1,147 @@ +{'date': 1763403146.4774234, 'confidence': 0.13, 'cve': 'CVE-2025-59294', 'change_count': 59, 'file': 'dwmapi.dll', 'kb': 'KB5066835', 'patch_store_uid': '90c2c377-0120-4522-801b-0bb067d94c1b'} +-------------------------------------------------------------------- +CVE-2025-59294 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows dwmapi.dll – client-side IPC wrapper classes CApiPortClient and +CPortClient that are used by high-level public DWM APIs such as +Dwm​SetIconicLivePreviewBitmap, DwmEnableBlurBehindWindow, +DwmQueryThumbnailSourceSize, etc. + + +Vulnerability Class +-------------------------------------------------------------------- +Information Disclosure / Exposure of Sensitive Information to an +Unauthorized Actor (CWE-200) caused by returning a pointer to +uninitialised or previously-freed shared memory that may contain data +belonging to other callers or to the Desktop Window Manager process. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. DWM client code stores application–supplied extra data (bitmaps, + thumbnails, colourisation parameters, etc.) in a per-connection + "extra data" section that is owned by the CPortClient/CApiPortClient + object: + * the base address of that section is kept in field + this+0x08 ( this[1] in the de-compiler listing), and + * its current committed length is kept in *this (DWORD at + offset 0). + +2. Old implementation of + CApiPortClient::LockExtraDataPointer(uint size, void **out) + (see before-patch listing) is trivial: + • it merely checks that size <= *this ; + • then calls EnsureExtraDataSection(), which may create or grow + the section but never initialises the new bytes; and finally + • returns the raw section base pointer through *out. + +3. Any caller that requests less than the real section size therefore + receives a pointer that covers not only its own data but also + bytes that belong to previous callers. Those bytes are left in an + un-initialised state (or contain data from a prior bitmap copy) and + can be read back by an un-privileged application. Typical leaked + data include window thumbnails, live-preview frames, colourisation + buffers and other visual artefacts handled by dwmapi on behalf of + other windows and/or other processes. + +4. Because the section is created with PAGE_READWRITE | SEC_COMMIT and + mapped into every process that uses the DWM public API, the problem + becomes a cross-process information disclosure: any application can + • connect to the DWM ALPC port, + • call LockExtraDataPointer() with a small size, and + • read past the valid range to obtain pixels or other data that + were written by a different window-owner. + +5. The same section is freed in CPortClient::~CPortClient. In the + original code the destructor blindly HeapFree()s the buffer even if + other threads still hold the pointer returned by + LockExtraDataPointer(), turning the info-leak into a potential + use-after-free as well. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// === before patch === +__int64 CApiPortClient::LockExtraDataPointer(uint sz, void **pp) +{ + if (sz > *(DWORD*)this) + return E_INVALIDARG; + EnsureExtraDataSection(this); // zero-initialisation missing + *pp = *((void **)this + 1); // raw base pointer + return 0; +} + +// === after patch === +if (FeatureEnabled()) { + rc = EnsureConnected(this); + rc = CPortClient::AcquireExtraData(this->pPort, sz, pp); // bounded +} else { + // legacy path (see above) +} +``` +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls a public DWM API that ends up in + CApiPortClient::LockExtraDataPointer(). +2. Function returns the base of the shared extra-data section + regardless of the requested size. +3. Attacker reads beyond the end of its own buffer and obtains + uninitialised or previously-freed data created by another window or + another process. + + +Attack Vector +-------------------------------------------------------------------- +A user-mode process running under the current desktop session simply +calls documented DWM APIs (e.g. DwmSetIconicLivePreviewBitmap followed +by DwmQueryThumbnailSourceSize) and then reads from the pointer +returned by LockExtraDataPointer() past the requested size. +No special privileges are required; physical access is not necessary +contrary to the original advisory wording. + + +Patch Description +-------------------------------------------------------------------- +Microsoft introduced a runtime feature-flag (Feature_2578215227) that +activates a new, safe code path: +1. LockExtraDataPointer now calls CPortClient::AcquireExtraData(), which + allocates exactly the number of bytes requested and zero-initialises + them in the DWM process before mapping them into the caller. +2. The raw section base pointer is no longer returned; callers receive + only the bounding pointer supplied by AcquireExtraData. +3. In the destructor of CPortClient the buffer is freed only when the + feature flag is disabled; under the new path the memory lifetime is + managed by DWM and not released in user-mode, eliminating the + dangling-pointer scenario. +4. Several public-API entry points were refactored to pass the client + CApiPortClient handle explicitly, ensuring that the new allocation + rules are consistently applied. + + +Security Impact +-------------------------------------------------------------------- +Before the patch any un-privileged process could read arbitrary bytes +from the DWM shared extra-data section, obtaining window thumbnails and +live-preview frames belonging to other windows and potentially other +users, constituting an information-disclosure vulnerability tracked as +CVE-2025-59294. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched logic enforces: +• Strict size-bounded allocations via AcquireExtraData(); +• Zero-initialisation of newly-committed memory; +• No exposure of the entire shared section base pointer to callers; +• Elimination of premature HeapFree() by delegating lifetime management + to the ALPC service side when the feature is enabled. + +These changes close both the out-of-bounds read and the dangling +pointer, effectively mitigating the information disclosure. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-49734_powershell.exe.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-49734_powershell.exe.txt new file mode 100644 index 0000000..77c2e65 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-49734_powershell.exe.txt @@ -0,0 +1,142 @@ +{'cve': 'CVE-2025-49734', 'kb': 'KB5065426', 'change_count': 4, 'confidence': 0.19, 'date': 1757835035.0649962, 'file': 'powershell.exe', 'patch_store_uid': '1a748dde-bce7-4e09-9cd0-cc04adfec7a2'} +-------------------------------------------------------------------- +CVE-2025-49734 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows PowerShell (powershell.exe) – command-line argument and +configuration‐string parser. Affected routines: + • sub_14000626C (renamed to + NativeMsh::PwrshCommon::VerifyDOTNetVersionFormat) + • sub_140001EE0 (renamed to ParseCommandLineArguments) + • sub_140001410 (renamed to wmain) + +Vulnerability Class +-------------------------------------------------------------------- +Improper input validation / stack-based buffer overflow leading to +local privilege escalation (CWE-20, CWE-121). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Unpatched powershell.exe validates the string that represents the +required .NET / PowerShell engine version in several ad-hoc code +paths (sub_140001EE0 and wmain). The legacy helper sub_14000626C is +supposed to vet this string, but it only checks that the last +extension equals “.psc1” and returns a boolean without touching the +caller-supplied out-parameters. + +ParseCommandLineArguments and wmain therefore re-parse the same +string themselves. They store every numeric component that appears +between dots into a fixed-length local array: + + int VersionParts[4]; // v66 in wmain + +The index used for that array is cbData[0] which is incremented for +*every* successfully parsed component, yet it is never range-checked +(only a loose i<=2 loop guard is applied, but cbData[0] itself is +still used as an index). A malicious version string containing more +than three dot-separated fields (e.g. “9.9.9.9.9…”) therefore makes +the write + + VersionParts[cbData[0]] = wcstoul(...); + +overflow the 4-element stack buffer, corrupting adjacent saved frame +state and enabling execution control inside a highly-privileged +powershell.exe instance (PowerShell Direct launches it as LOCAL +SYSTEM inside the guest). + +Patch introduces NativeMsh::PwrshCommon::VerifyDOTNetVersionFormat +and rewrites ParseCommandLineArguments/wmain so that ALL external +version strings flow through this single routine. The new checker +safely: + • zeros out the caller-supplied out-parameters + • rejects empty or NULL strings + • enforces max three ‘.’ separators + • strips leading zeros + • limits each numeric field to 10 digits and INT_MAX + • verifies the wcstoul end-pointer +On any deviation it returns 0 and the caller aborts the start-up +sequence. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old sub_14000626C – only checks for “.psc1” +bool __fastcall sub_14000626C(__int64 a1) +{ + const wchar_t *v2; // uninitialised copy of a1 + bool ok = 0; + wchar_t *dot; + int cmp; + if (sub_140003730(a1,a1) || // NULL / empty? + (dot = wcsrchr(v2,'.'))==0 || // no dot + (cmp = CompareStringW(0x7F,1,dot,-1,L".psc1",5))==0 || + (ok = (cmp==2), cmp!=2)) // accept only “.psc1” + { + Log(26,a1); + } + return ok; // out-parameters unused +} +``` +```c +// new VerifyDOTNetVersionFormat – tight format checking +bool __fastcall VerifyDOTNetVersionFormat(..., + const unsigned __int16 *pszVer, + unsigned int *Major, + int *Minor) +{ + *Major = *Minor = -1; + if (StringIsNullOrEmpty(pszVer)) return 0; + const wchar_t *dot = wcschr(pszVer,'.'); + if (!dot) return 0; + // strip leading zeros, max 10 digits + ... + *Major = wcstoul(...); + for (int i=0;i<=2;++i) { // at most three dots + // repeat parsing, same length & range checks + } + *Minor = array[0]; + return 1; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker launches powershell.exe (e.g. through PowerShell Direct) + with a crafted “-version” or manipulates HKLM\…\PowerShellVersion. +2. wmain -> ParseCommandLineArguments copies the string into the + stack array v66[4] without bounds checks (pre-patch). +3. More than four components overwrite the stack frame, allowing + arbitrary code execution in the SYSTEM process. + +Attack Vector +-------------------------------------------------------------------- +Local attacker who can start PowerShell Direct or influence the +registry values read during start-up supplies an over-long version +string such as “9999999999.9999999999.9999999999.9999999999”. + +Patch Description +-------------------------------------------------------------------- +1. Introduced VerifyDOTNetVersionFormat() that performs strict, single + point validation and sets out-parameters predictably. +2. ParseCommandLineArguments rewritten to call the new helper rather + than hand-rolling the parse. +3. wmain updated accordingly; all manual loops and the fixed-size + VersionParts buffer removed. +4. Related helper names were clarified and parameter lists fixed to + use typed pointers instead of loosely-typed _DWORD/LPBYTE. + +Security Impact +-------------------------------------------------------------------- +Before the patch a non-admin user inside the guest could corrupt the +stack of the SYSTEM-owned powershell.exe process and execute code +with elevated privileges, leading to a full local elevation of +privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +The new helper rejects every malformed input early; array writes have +been eliminated; out-of-range fields or too many components now cause +powershell.exe to abort start-up. No uncontrolled write remains, +removing the exploitation primitive. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53799_windowscodecs.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53799_windowscodecs.dll.txt new file mode 100644 index 0000000..2977248 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53799_windowscodecs.dll.txt @@ -0,0 +1,138 @@ +{'confidence': 0.29, 'patch_store_uid': '8a014453-9805-479e-8b6a-7f5f1c3c9151', 'change_count': 357, 'kb': 'KB5065426', 'cve': 'CVE-2025-53799', 'date': 1757843629.7153378, 'file': 'windowscodecs.dll'} +-------------------------------------------------------------------- +CVE-2025-53799 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Imaging Component (WIC), old JPEG decoder (WICOldJPEGLib) in +windowscodecs.dll. Vulnerable logic lives in the input-controller +initialisation and reset routines. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-908: Use of Uninitialized Resource (wrong function pointer leads to +invocation of routine with wrong prototype, resulting in use of +uninitialized arguments and memory reads). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The JPEG input controller is allocated at offset 0x544 inside the main +codec object. The structure layout is: + +0x00 consume_markers (func ptr) + +0x08 reset_input_controller (func ptr) + +0x10 start_input_pass (func ptr) + +0x18 finish_input_pass (func ptr) + +0x20 state fields (word, byte, …) + +During initialisation (jinit_input_controller) the code allocates 48 +bytes for the controller and fills the dispatch table. In the buggy +version it assigns the reset_input_controller pointer to +sub_1467A91A0, which is actually +WICOldJPEGLib::h2v1_fancy_upsample_intellib – a helper that expects the +prototype + void **func(__int64 a1, __int64 a2, __int64 a3, __int64 *a4) + +At run-time, WIC later calls + controller->reset_input_controller(codec_object); +passing *only* RCX (a1). The callee therefore executes with RDX, R8 +and R9 still holding the transient values left by the call-site. The +function immediately dereferences these registers: + *(unsigned int *)(a2 + 40); // a2 is uninitialised + *(_QWORD *)(a3 + 8 * i); // a3 is uninitialised + *a4; // a4 is uninitialised pointer + +The out-of-contract access leaks process memory through the +ippiSampleUpRowH2V1_Triangle_JPEG call chain, creating an information +exposure primitive. On some builds it can also crash the process when +invalid pages are touched. + +The patched build replaces the wrong pointer with a new function that +matches the single-argument contract and explicitly re-initialises the +controller state: + *v1 = consume_markers; + *(WORD) = 0; + *(BYTE) = 1; + call consume_markers(); + call next_state(); + codec->some_field = 0; + +No uninitialised arguments are used. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable assignment (before) +*(_QWORD *)(result + 8) = sub_1467A91A0; // wrong prototype + +// buggy function that will be called with 1 arg instead of 4 +void **h2v1_fancy_upsample_intellib(__int64 a1, __int64 a2, + __int64 a3, __int64 *a4) +{ + for (i = 0; i < *(DWORD *)(a1 + 468); ++i) + g_pfnIppiSampleUpRowH2V1_Triangle_JPEG_8u_C1( + *(QWORD *)(a3 + 8*i), // uses uninitialised a3 + *(DWORD *)(a2 + 40), // uses uninitialised a2 + *(QWORD *)(*a4 + 8*i)); // uses uninitialised a4 +} + +// fixed assignment (after) +*(_QWORD *)(result + 8) = reset_input_controller; // correct function + +// safe reset function (after) +__int64 reset_input_controller(__int64 a1) +{ + v1 = *(QWORD *)(a1 + 544); + *(QWORD *)v1 = consume_markers; + *(WORD *)(v1+32) = 0; + *(BYTE *)(v1+40) = 1; + (*(func*)(*(QWORD*)a1 + 32))(a1); + result = ((func*)(a1+552))[0](a1); + *(QWORD *)(a1 + 176) = 0; + return result; +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Application decodes a JPEG via WIC. +2. jinit_input_controller() runs and stores wrong function pointer at + controller+0x08. +3. Decoder later calls controller->reset_input_controller(codec). +4. h2v1_fancy_upsample_intellib executes with only RCX initialised. +5. The function reads from uninitialised RDX/R8/R9, disclosing memory + contents to the output buffer or causing a crash. + +Attack Vector +-------------------------------------------------------------------- +A local attacker provides a crafted JPEG that is decoded by any +application relying on WIC (e.g., Windows Photos, Explorer thumbnails). +No special privileges are required; code execution is not needed. + +Patch Description +-------------------------------------------------------------------- +1. Added new routine reset_input_controller that adheres to the single + parameter calling convention and safely re-initialises internal + state. +2. jinit_input_controller now assigns the reset_input_controller + pointer to this new function instead of the unrelated up-sampling + helper. +3. The obsolete h2v1_fancy_upsample_intellib implementation is no + longer reachable through the input controller dispatch table. + +Security Impact +-------------------------------------------------------------------- +Before the patch, arbitrary stack/register residue could be used as +pointers, allowing information disclosure and potential denial of +service in any process that decodes untrusted JPEGs with WIC. The +issue is classified as an information disclosure vulnerability +(CVE-2025-53799). + +Fix Effectiveness +-------------------------------------------------------------------- +The dispatch table now points to a correctly prototyped function that +uses only the provided parameter and explicitly zeros state fields, +eliminating the uninitialised memory access. No remaining code paths +appear to reference the old helper, so the fix is believed to be +complete, although full binary-wide auditing was not performed (state +coverage outside the diff is unknown). diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53800_d2d1.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53800_d2d1.dll.txt new file mode 100644 index 0000000..f618f23 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53800_d2d1.dll.txt @@ -0,0 +1,118 @@ +{'change_count': 54, 'patch_store_uid': 'b1a2e097-49e5-4833-9ad9-f12764389b5b', 'cve': 'CVE-2025-53800', 'kb': 'KB5065426', 'confidence': 0.16, 'date': 1757843797.467048, 'file': 'd2d1.dll'} +-------------------------------------------------------------------- +CVE-2025-53800 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +D2D1.dll (Windows Graphics Component) +Class CDImageContext, method +CDImageContext::Initialize(ID3D11Device3*, IDeviceInternal*, DebugSink*, +CTickCounter*, uint) + +Vulnerability Class +-------------------------------------------------------------------- +Incorrect initialization / logic error (CWE-1419) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the Initialize() routine pulls an internal device +parameter it calls the "max tile size" directly from an offset inside an +object returned by the caller–controlled IDeviceInternal interface. The +sequence is: + 1. The attacker-supplied IDeviceInternal* (arg a3) is stored at + this+0x1228 (v7). + 2. A virtual call through *(v7)->vtable[8] returns a pointer to an + internal CD3DDeviceCommon object (call site: *(v7)+0x40). + 3. The code then reads a dword at offset +0x90 (decimal 144) of that + object, assumes it is a valid "tile size", and truncates it to at + most 0x1000: + v17 = *(DWORD *)(deviceCommon + 0x90); + v18 = v17 < 0x1000 ? v17 : 0x1000; // stored in this+0xF0 + +No attempt is made to verify that the returned object is really a +CD3DDeviceCommon or that the offset contains an initialized value. An +attacker that implements IDeviceInternal can therefore return a forged +object with arbitrary contents. Because later parts of CDImageContext +use the field at this+0xF0 to size allocator buckets and tiling buffers, +setting the value to 0 or to an overly large number leads to: + • Division-by-zero or integer-underflow paths. + • Out-of-bounds memory accesses when buffer sizes are smaller than the + amount of data copied. + • Memory corruption inside a high-integrity process using D2D1.dll, + which can be leveraged for local elevation of privilege. + +Additional foot-fault: the code stored the incoming ID3D11Device3* (arg + a2) into a CComPtr that was declared as ID2D1EffectContext*, leading to +mismatched vtables if the pointer was later dereferenced. + +Patch changes: + • Replaces the raw field read with the vetted helper + MaxTileSize = CD3DDeviceCommon::GetMaxTileSize(deviceCommon); + which returns a device-validated, clamped value. + • Removes the constant 0x1000 truncation logic. + • Corrects the mis-typed CComPtr assignment (now uses + CComPtr<ID3D12Resource>). + • Tightens error paths (LABEL_20 -> LABEL_18) but functional changes + are limited to the two bullets above. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable code (before) +DWORD raw = *(_DWORD *)(deviceCommon + 0x90); // untrusted read +DWORD max = 4096; +if (raw < 0x1000) + max = raw; +this->m_MaxTileSize = max; // no validation + +// patched code (after) +CD3DDeviceCommon *dev = GetDeviceCommon(v7); +DWORD max = CD3DDeviceCommon::GetMaxTileSize(dev); // validated helper +this->m_MaxTileSize = max; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker provides a fake IDeviceInternal object to a privileged + component that eventually calls CDImageContext::Initialize(). +2. Fake object returns an attacker-controlled pointer from its vtable[8]. +3. Initialize() blindly reads DWORD at offset +0x90. +4. Value is propagated to this->m_MaxTileSize and later used for buffer + allocation / copy sizes. +5. Crafted value causes memory corruption, leading to code execution in + the context of the caller (high integrity or SYSTEM). + +Attack Vector +-------------------------------------------------------------------- +Local. The attacker must be able to load or inject code into a process +that calls the Direct2D image pipeline with an attacker-controlled +IDeviceInternal implementation (e.g., via COM registration or a plug-in +surface). No additional privileges are required beyond the ability to +start such a process. + +Patch Description +-------------------------------------------------------------------- +1. Replaces direct structure-member access with the accessor + CD3DDeviceCommon::GetMaxTileSize(), guaranteeing that the returned + value is initialized, range-checked, and device-appropriate. +2. Removes the hard-coded 0x1000 truncation logic. +3. Fixes an unrelated but unsafe CComPtr assignment that stored an + ID3D11Device3* into a pointer typed as ID2D1EffectContext*. + +Security Impact +-------------------------------------------------------------------- +Exploitation yields arbitrary memory corruption in a privileged graphics +process, which can be converted to local elevation of privilege. +Microsoft rates the issue as an Elevation of Privilege vulnerability +(CVE-2025-53800). + +Fix Effectiveness +-------------------------------------------------------------------- +The new helper function abstracts and validates the tile-size value, +eliminating the direct read of possibly uninitialized or attacker- +controlled memory. It also removes the faulty constant 0x1000 logic. +Given that all later uses consume the validated field, the immediate +root cause is addressed. Remaining risk would stem only from other code +paths that may still dereference internal objects without validation; no +such occurrences were observed in the supplied diff. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53800_dxgi.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53800_dxgi.dll.txt new file mode 100644 index 0000000..1819d78 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53800_dxgi.dll.txt @@ -0,0 +1,130 @@ +{'cve': 'CVE-2025-53800', 'patch_store_uid': 'ce779ff9-268d-4bc0-b7f2-70c9427854b5', 'kb': 'KB5065426', 'file': 'dxgi.dll', 'change_count': 94, 'confidence': 0.12, 'date': 1757843884.454682} +-------------------------------------------------------------------- +CVE-2025-53800 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Graphics Component – user-mode DXGI library +(dxgi.dll). Impacted routines are mainly: + • AslpFileGetVersionBlock + • CDXGISwapChain::OnSettingsChanged + • CDXGISwapChain::RegisterPowerNotificationCallback + • ATL::CComCachedTearOffObject<…>::Release +The vulnerable code was delivered in the public Windows graphics +stack and is reachable from several graphics and App-X processing +paths that run inside privileged processes such as dwm.exe or +services using dxgi.dll. + +Vulnerability Class +-------------------------------------------------------------------- +Incorrect initialization / use of an un-initialised resource +(CWE-1419) that results in a logic error and memory corruption which +can be leveraged for local privilege-escalation. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The defect sits in AslpFileGetVersionBlock(), a helper that locates +and validates the VS_VERSION_INFO resource inside an image or data +file: + +1. The code maps the caller-supplied file and calls + LdrResSearchResource() to obtain a pointer (Src[0]) to the + version block. +2. A range check then validates that this pointer belongs to the + mapped view: + if (Src[0] < Base || Src[0] > Base + Size) + … error … + The upper bound ( Base + Size ) is calculated with the helper + variable v18 . +3. In the pre-patch build v18 is declared as coming from RCX – the + first function parameter – instead of R8 (the register that will + later hold the mapped view-size). Consequently v18 is left + un-initialised *or* contains an unrelated pointer value before it + is eventually overwritten in some, but **not all**, code paths. +4. When the LdrResSearchResource() fast path succeeds, the pointer + check executes while v18 still contains this stale value. By + crafting Src[0] so that it falls below Base + bogusSize an + attacker can bypass the range check and force the routine to + treat an out-of-bounds buffer as a legitimate version block. +5. The function later trusts that buffer and copies/parses + controlled data, leading to memory corruption inside the caller’s + process (often running with SYSTEM integrity in servicing or + graphic-worker contexts). + +Additional issues fixed in the same patch set (null pointer guards in +CDXGISwapChain::OnSettingsChanged, correct reference counting in +ATL::CComObjectNoLock::Release, simplified registration in +RegisterPowerNotificationCallback) were not strictly required for the +privilege escalation but hardened the affected code paths. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch (dxgi.dll) +void *v18; // rcx <- wrong register, never initialised +... +if (Src[0] < v10 || Src[0] > &v10[(size_t)v18]) + // supposed range check, can be bypassed +``` +```c +// patched +void *v18; // r8 <- correct register (mapped view size) +... +if (Src[0] < v10 || Src[0] > &v10[(size_t)v18]) + // now operates on initialised length value +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-privileged attacker causes a privileged component (for example + app-installer service or dwm.exe) to call AslpFileGetVersionBlock() + on a file they control. +2. Crafted file arranges VS_VERSION_INFO so that Src[0] points outside + the mapped image yet still passes the faulty upper-bound test. +3. Subsequent parsing operates on out-of-range memory, corrupting the + process heap/image and enabling code-execution with the victim + process privileges (SYSTEM or administrator SYSTEM-context). + +Attack Vector +-------------------------------------------------------------------- +Local – the attacker needs the ability to feed a malicious PE or +resource file that is later processed by a privileged dxgi.dll +consumer (e.g. graphics service, signed-package installer, or another +high-integrity process invoking graphics resource APIs). +No additional authentication is required once the file is picked up. + +Patch Description +-------------------------------------------------------------------- +Microsoft corrected the initialisation defect by: +1. Declaring v18 as the correct R8-sourced variable so that it + always contains the mapped view size before the pointer range check + executes. +2. Adding new error-handling labels (LABEL_38/39) to ensure early exit + paths free the mapping correctly. +3. Introducing Feature_EnsureVerBlockCanHoldFixedInfo() gated logging + that makes the validation stricter. +4. Complementary hardening: null-pointer checks in + CDXGISwapChain::OnSettingsChanged, SafeUnknownDecrementReference() + usage in Release(), and a minimalistic implementation of + RegisterPowerNotificationCallback. + +Security Impact +-------------------------------------------------------------------- +Because the range check could be bypassed the routine would operate on +attacker-controlled memory, enabling arbitrary memory read/write and +ultimately execution of attacker code in a privileged graphics +context. Once code execution is achieved the attacker can elevate +from a standard user to SYSTEM, fulfilling the "Elevation of +Privilege" impact noted in CVE-2025-53800. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch addresses the root cause by ensuring the size variable used +in the boundary calculation is always initialised with the correct +view length. Pointer validation is therefore reliable and out-of- +range version blocks are rejected with STATUS_INVALID_IMAGE_FORMAT. +No alternate path that re-introduces the stale variable has been +observed, and additional guard code (Feature_EnsureVerBlock… and +extra logging) increases defence in depth. The fix is considered +adequate. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_bthport.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_bthport.sys.txt new file mode 100644 index 0000000..dd71e03 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_bthport.sys.txt @@ -0,0 +1,126 @@ +{'patch_store_uid': '756030a2-be7b-4bcc-b9d7-15668181f7d5', 'change_count': 11, 'cve': 'CVE-2025-53802', 'kb': 'KB5065426', 'confidence': 0.26, 'file': 'bthport.sys', 'date': 1757843768.2435308} +-------------------------------------------------------------------- +CVE-2025-53802 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel-mode Bluetooth driver (bthport.sys) +AdvertisingRequestControllerModule and related helper classes that +manage per-client advertising-request state. + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free (CWE-416) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +• Every user-mode handle to the Bluetooth device is represented in + the kernel by a WDFFILEOBJECT structure. Advertising requests are + tracked per file object. + +• Prior to the patch the object at offset +816 in + AdvertisingRequestControllerModule was uninitialised. Several + helper routines (utl::_HashTable<WDFFILEOBJECT* …>) expected this + field to be a doubly-linked list / hash bucket that maps a + WDFFILEOBJECT* to a vector<AdvertisingRequestProgrammer:: + IrkRefCountToken>. + +• Because the constructor did not initialise the spin-lock or list + head, and OnFileObjectCleanup(FileObject const &) was entirely + missing, entries associated with a closing file handle were never + removed. After the user closed the handle the corresponding + WDFFILEOBJECT was freed by the framework, but pointers to that + object remained inside the global hash table. + +• Later operations (e.g. generation of new advertising addresses in + AdvertisingRequestProgrammer::GetNewAddress) iterate this table, + dereference the stale WDFFILEOBJECT pointer and pass it further + down the stack. At this point the memory is already under control + of the pool allocator, allowing an attacker to reuse it and obtain + arbitrary kernel-memory access. + +• The access happens in an arbitrary context (often DPC) and runs in + kernel mode, enabling escalation from normal user privileges to + SYSTEM. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old constructor – critical fields left uninitialised +*((_QWORD *)this + 97) = 0; // spinlock never set +*((_QWORD *)this + 101) = 0; // list head garbage +... + +// new constructor – proper initialisation +KeInitializeSpinLock((PKSPIN_LOCK)this + 101); +*((_QWORD *)this + 102) = (char*)this + 816; // fwd link +*((_QWORD *)this + 103) = (char*)this + 816; // back link +*((_WORD *)this + 424) = 2048; // hash flags +``` + +```c +// new OnFileObjectCleanup() +wil::acquire_kspin_lock(v8, this+808); +... +find(this+816, &v12, &m_Object); +if (v12 != this+816) { + vector::operator=(local_copy, v12+32); // copy out + erase(this+816, &m_Object, v12); // unlink entry +} +release_lock(); +_RangeDestroyApc(..., local_copy); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User opens handle to "\\.\BthLEDevice" (or similar). +2. Sends IOCTL 0x4112D8 (4264680 – periodic advertising request) or + the newer 0x4112EC (4264708 – generate advertising address). +3. Driver allocates entry in internal hash table that references the + WDFFILEOBJECT. +4. User closes the handle; Framework frees the WDFFILEOBJECT. +5. Because old driver lacks cleanup, the pointer stays in the table. +6. Any later advertising operation traverses the table and touches the + freed memory – use-after-free occurs in kernel context. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. Requires opening a Bluetooth device +handle, sending specific IOCTLs, then closing the handle while +triggering a second Bluetooth action. No special privileges are +needed beyond the ability to communicate with the Bluetooth device +interface. + +Patch Description +-------------------------------------------------------------------- +1. Constructor now initialises a spin lock and properly anchors the + WDFFILEOBJECT->token hash list; flags set to 0x800 to mark list as + circular. +2. Added fully-featured + AdvertisingRequestControllerModule::OnFileObjectCleanup() which: + • acquires module spin lock + • finds the hash-table node matching the closing WDFFILEOBJECT + • copies and destroys the associated vector while holding no lock + • erases the node and releases the lock. +3. Destructor walks the remaining list under the same lock and frees + any still-allocated nodes, preventing leaks and future UAF. +4. GetNewAddress() and RequestForNewRandomAddress() were rewritten to + use IrkRefCountToken helpers that maintain their own lifetime, + removing more raw pointer usage. + +Security Impact +-------------------------------------------------------------------- +The stale pointer dereference allowed controlled pool reuse leading to +arbitrary kernel memory access and ultimately elevation of privilege +from the caller to NT AUTHORITY\SYSTEM (ring-0). The issue is +therefore an EoP in the Windows Bluetooth service. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch: (a) guarantees that the per-fileobject entry is removed on +cleanup, (b) introduces proper locking and list initialisation, and +(c) frees any residual entries in the destructor. These corrections +eliminate dangling references and make a post-close dereference +impossible, effectively fixing the UAF. + diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_bthserv.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_bthserv.dll.txt new file mode 100644 index 0000000..e874a8b --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_bthserv.dll.txt @@ -0,0 +1,117 @@ +{'change_count': 25, 'date': 1757843800.4766874, 'kb': 'KB5065426', 'cve': 'CVE-2025-53802', 'patch_store_uid': '6bbd1ebb-0832-4b00-aa09-70baf89db1ad', 'confidence': 0.52, 'file': 'bthserv.dll'} +-------------------------------------------------------------------- +CVE-2025-53802 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Bluetooth Service (bthserv.dll) – internal helper class +SdpStack used during SDP (Service Discovery Protocol) parsing. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SdpStack is a grow-able LIFO container that keeps traversal state +(_tag, _SDP_NODE *, LIST_ENTRY *) in 24-byte records. The object +layout (64-bit) is effectively: + +0x00 QWORD EntryCount ; number of valid elements + +0x08 QWORD pTop ; pointer to first free slot + +0x10 QWORD pBase ; pointer to beginning of buffer + +0x18 QWORD pStaticBuffer ; pointer to small static buffer + +0x20 DWORD Capacity ; number of records that fit + +When Push() detects that the stack is full it allocates a new buffer +(double size), copies existing entries, updates the object pointers +and frees the old heap buffer with MIDL_user_allocate_0(). The buggy +implementation (pre-patch) executed the free *before* eliminating all +other references to the old allocation: + +1. pTop was copied into a local (this[1]) **before** reallocation. +2. New memory was allocated and pTop was advanced into the *new* + buffer, but the local alias still pointed inside the *old* buffer. +3. The old buffer was freed. +4. The function continued to use the stale local alias when the record + was written, resulting in a write into freed memory. + +If an attacker could cause the heap to be reused between steps 3 and +4, controlled data would be written into an arbitrary kernel pool +object, giving a classic pool-based UAF. + +The additional, cosmetic feature-flag gating in the old version masked +this behaviour for some configurations, so the bug manifested only +when Feature_1723606328 was *disabled* (the common production case). + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch excerpt (simplified) +if (this[1] == (char*)this[2] + 24 * Capacity) { + v9 = MIDL_user_allocate_1(24 * 2 * Capacity); + memcpy(v9, this[2], 24 * Capacity); + this[1] = &v9[24 * Capacity]; // pTop -> new buffer + v11 = (size_t)this[2]; // old buffer ptr + if ((void*)v11 != this[3]) + MIDL_user_allocate_0(v11); // FREE OLD BUFFER + this[2] = v9; // pBase -> new buffer + ... +} +*(_DWORD*)this[1] = Tag; // write via STALE this[1] +*((_QWORD*)this[1] + 1) = pNode; +*((_QWORD*)this[1] + 2) = pList; +``` + +```c +// patched excerpt +v6 = (char*)*((QWORD*)this + 1); // take fresh pTop +... +MIDL_user_allocate_0(oldBase); // free old buffer +v6 = (char*)*((QWORD*)this + 1); // REFRESH POINTER +*(DWORD*)v6 = Tag; // write into valid memory +*(QWORD*)(v6 + 8) = pNode; +*(QWORD*)(v6 +16) = pList; +*((QWORD*)this +1)+=24; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User mode supplies a crafted SDP record -> bthserv parses it -> +BthQueryRemoteServices() -> internal parser creates SdpStack instance +-> repeated Push() calls while traversing attacker-controlled data +-> buffer grows -> reallocation path triggers UAF -> arbitrary pool +overwrite -> privilege escalation within bthserv (runs as LocalSystem). + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to issue Bluetooth SDP queries to the +host (for example by running in a standard user context with Bluetooth +access) sends specially crafted, deeply nested SDP data that forces the +service to grow the stack. + +Patch Description +-------------------------------------------------------------------- +The update fixes pointer lifetime management in SdpStack::Push(): +1. Re-typed the object pointer so the compiler treats it as mutable. +2. Stores the new pTop in a temporary *after* the old buffer is + released, ensuring no stale alias survives the free. +3. Removes dual code paths gated by Feature_1723606328, eliminating the + configuration in which the stale pointer was used. +4. Minor clean-ups of variable types to 64-bit safe forms. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a local attacker could reliably obtain an arbitrary +kernel pool write primitive from user mode, leading to elevation of +privilege to SYSTEM. The bug is exploitable without crashing the OS +because the freed block is immediately re-used by subsequent kernel +allocations. + +Fix Effectiveness +-------------------------------------------------------------------- +The refreshed pointer after the free eliminates the dangling alias, so +no writes are performed on freed memory. Independent code review of +all write sites in Push() shows they now reference the up-to-date pTop +value. No remaining path frees the buffer while outstanding aliases +exist, so the UAF condition is closed. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_microsoft.bluetooth.service.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_microsoft.bluetooth.service.dll.txt new file mode 100644 index 0000000..ac5bd7a --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53802_microsoft.bluetooth.service.dll.txt @@ -0,0 +1,110 @@ +{'file': 'microsoft.bluetooth.service.dll', 'confidence': 0.27, 'cve': 'CVE-2025-53802', 'change_count': 110, 'patch_store_uid': '67b1359b-3787-4b70-8a1b-2e864ed2399c', 'date': 1757843802.7197566, 'kb': 'KB5065426'} +-------------------------------------------------------------------- +CVE-2025-53802 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Bluetooth Service (microsoft.bluetooth.service.dll), +class Microsoft::Bluetooth::Core::Interface::GapAdvertisementPublisherImpl + + +Vulnerability Class +-------------------------------------------------------------------- +Use-After-Free caused by missing synchronization (race condition) +(CWE-416) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GapAdvertisementPublisherImpl exposes multiple WinRT property +getters/setters (Anonymous, Connectable, Legacy, IncludeTxPower and +Priority). Prior to the patch every accessor directly read or wrote +object fields: + * byte flags at offsets +0x190, +0x191, +0x192, +0x193 + * enum GapAdvertisementPublisherPriority at +0x210 +without acquiring the object’s internal SRW lock located at +0xA0 +(object+160). When two threads manipulated the same publisher +instance one thread could destroy/free the object (e.g. via Stop() or +Release()) while the other thread was still executing one of these +property functions. Because the accessors performed raw pointer +arithmetic ( *((_BYTE*)this+402) etc. ) the code followed a dangling +pointer after the object memory had been returned to the heap. Any +subsequent heap reuse turned the stale read/write into memory +corruption under the high-privilege Bluetooth service, providing a +reliable elevation-of-privilege primitive. + +Impacted members and offsets + Anonymous : byte *(this+0x192) + Connectable : byte *(this+0x190) + Legacy : byte *(this+0x191) + IncludeTxPower : byte *(this+0x193) + Priority : dword *(this+0x210) +All five were reachable by an authenticated local user through public +WinRT Bluetooth APIs. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – no locking, raw access +__int64 put_Connectable(GapAdvertisementPublisherImpl *this, char v) +{ + *((_BYTE*)this + 400) = v != 0; // write without lock + return 0; +} + +__int64 get_Connectable(GapAdvertisementPublisherImpl *this, UCHAR *out) +{ + if (!out) return E_POINTER; + *out = *((_BYTE*)this + 400); // read without lock + return 0; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Client app obtains GapAdvertisementPublisher object via WinRT API. +2. Thread A calls put_*/get_* repeatedly. +3. Thread B concurrently closes the same object, freeing memory. +4. Thread A continues execution in accessor, uses freed memory -> UAF. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker able to run user-mode code and access the +public Windows Bluetooth WinRT APIs. No additional privileges are +required; exploitation occurs entirely in user-mode but within the +SYSTEM-level Bluetooth service process. + + +Patch Description +-------------------------------------------------------------------- +Each accessor now: +1. Tests a feature flag (Feature_2578215227). +2. If enabled, acquires the object’s SRW lock: + AcquireSRWLockExclusive for setters + AcquireSRWLockShared for getters +3. Performs the field access while the lock is held. +4. Releases the lock via RAII wrapper. +If the feature flag is disabled the previous unsynchronized path is +retained. + + +Security Impact +-------------------------------------------------------------------- +Unprivileged code could cause use-after-free in the SYSTEM Bluetooth +service, leading to memory corruption and elevation of privilege to +SYSTEM. Reliability is high due to deterministic field offsets and +controllable heap layout. + + +Fix Effectiveness +-------------------------------------------------------------------- +Locking all property accesses removes the race window that permitted +concurrent free/use, effectively preventing the UAF. The mitigation +relies on a feature flag; if that flag could be disabled the original +vulnerability would re-appear, but no such control is documented. +Overall the patch is technically sound and blocks the previously +exploitable condition. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53803_ntoskrnl.exe.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53803_ntoskrnl.exe.txt new file mode 100644 index 0000000..b4b2dd0 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53803_ntoskrnl.exe.txt @@ -0,0 +1,119 @@ +{'patch_store_uid': '8ceaf580-961a-430f-ad80-451621699df6', 'cve': 'CVE-2025-53803', 'file': 'ntoskrnl.exe', 'kb': 'KB5065426', 'change_count': 712, 'confidence': 0.13, 'date': 1757854155.5281885} +-------------------------------------------------------------------- +CVE-2025-53803 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel – Event Tracing for Windows (ETW) core logic, +specifically the function EtwpIsEventNameFilterEnabled in ntoskrnl.exe. +The routine is queried by user-mode providers to decide whether a +provider-defined name filter is active. + +Vulnerability Class +-------------------------------------------------------------------- +Race condition / time-of-check-to-time-of-use (TOCTOU) leading to use +of freed memory and kernel information disclosure (CWE-362 + CWE-416, +manifesting as CWE-209). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch EtwpIsEventNameFilterEnabled(…) + 1. Obtained the provider control block pointer: + pcb = *(a1 + 0x180) // a1 == WMI_LOGGER_CONTEXT + 2. Selected one of two filter pointers inside the 104-byte per- + provider slot (offset +56 or +96) based on the caller-supplied + flag ‘a5’. + 3. Immediately dereferenced that pointer in order to read: + BYTE NameCount = *(filter + 1); + QWORD AllowedMask = *(filter + 8); + QWORD RequiredMask= *(filter +16); + 4. No lock was held and the current IRQL remained unchanged. The + provider block can be modified or freed at PASSIVE_LEVEL by + another thread that is enabling/disabling the provider. + +Because the memory could be reclaimed between step (1) and the later +reads, EtwpIsEventNameFilterEnabled could end up reading uninitialised +or freed kernel heap memory. The values are returned to the caller as +part of the success/fail decision, permitting an authorized local +attacker to infer or disclose kernel heap contents and potentially +bypass KASLR or obtain other sensitive information. + +Structures/fields involved: + WMI_LOGGER_CONTEXT +0x180 : pointer to provider array + ProviderSlot[104] +56 / +96 : NAME_FILTER structure pointer + NAME_FILTER +1 : NameCount (BYTE) + NAME_FILTER +8 / +16 : QWORD masks examined by caller + +The absence of IRQL elevation or locking is therefore the fundamental +root cause. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v5 = *(QWORD *)(a1 + 384); +... +if (a5) + v9 = *(QWORD *)(v6 + v5 + 56); +else + v9 = *(QWORD *)(v6 + v5 + 96); +... +BYTE nameCnt = *(BYTE *)(v9 + 1); // use after potential free +``` +```c +// after +if (a3 < 2) { + oldIrql = KeGetCurrentIrql(); // raise to DISPATCH_LEVEL + __writecr8(2); + ... +} +// re-read pointers *after* raising IRQL +v16 = *(QWORD *)(a1 + 384); +... +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode ETW provider repeatedly calls + EventRegister()/EventUnregister() on one thread. +2. A second thread continuously queries the provider state through + EtwEventWrite or another API that internally reaches + EtwpIsEventNameFilterEnabled. +3. Window between provider disable (which frees filter memory) and the + second thread’s dereference causes freed memory read. +4. Returned status depends on stale heap data, disclosing up to + 16 bytes of kernel memory per call. + +Attack Vector +-------------------------------------------------------------------- +Local, authorized user can run two cooperating processes or threads +with standard ETW provider privileges. No elevated rights are needed +beyond the ability to start/stop an ETW session they control. + +Patch Description +-------------------------------------------------------------------- +1. Function signature changed (returns char, extra parameters). +2. If caller IRQL < DISPATCH_LEVEL and the fast path is chosen, + raises IRQL to 2 and handles KiIrqlFlags, preventing concurrent + frees that execute only at PASSIVE_LEVEL. +3. Re-reads the provider slot pointer and re-validates filter pointer + after IRQL raise, closing the TOCTOU window. +4. Restores original IRQL before exit. +5. Consolidates success logic into a local variable ‘v7’. + +Security Impact +-------------------------------------------------------------------- +Pre-patch an attacker could disclose kernel heap contents, undermining +KASLR and possibly assisting further exploitation. The issue is +classified as an information disclosure vulnerability with CVSS scope +limited to the local machine. + +Fix Effectiveness +-------------------------------------------------------------------- +The added IRQL elevation prevents provider filter memory from being +freed while it is being inspected, eliminating the race on supported +systems because memory management routines for provider blocks execute +only at PASSIVE_LEVEL. Re-validation of the pointer after elevation +further assures correctness. No residual uncontrolled dereferences +were observed in the patched code, so the fix appears effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53805_http.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53805_http.sys.txt new file mode 100644 index 0000000..cad9cad --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53805_http.sys.txt @@ -0,0 +1,114 @@ +{'cve': 'CVE-2025-53805', 'change_count': 77, 'confidence': 0.23, 'file': 'http.sys', 'patch_store_uid': 'b0281ae5-a0ca-45e6-8c4d-c6a4f9eace48', 'kb': 'KB5065426', 'date': 1757843504.5378437} +-------------------------------------------------------------------- +CVE-2025-53805 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +HTTP.sys kernel driver – request-serialization routines +(UlCopyRequestToBuffer / UlpCopyRequestInformation) + +Vulnerability Class +-------------------------------------------------------------------- +Out-of-bounds Read caused by use of an uninitialised stack +variable (CWE-125) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When user mode calls HttpReceiveHttpRequest / +HttpQueryRequestQueue the kernel builds a caller-supplied output +buffer that contains a variable number of sub-structures. The +function chain is + UlCopyRequestToBuffer() → UlpCopyRequestInformation(). + +UlpCopyRequestInformation first counts how many optional +sub-structures will be copied and puts that count in a local +variable. In the fast path (when all *UxKir* feature flags are +cleared) the following incorrect sequence is taken: + + v14 = (v11 != 0) + 4; // base count + … + v82 = UlChannelBindRequestInfoPresent(...); + v19 = v18 + 1; // <-- uses v18 + if (!v82) + v19 = v18; // again uses v18 + +The stack variable v18 is never initialised before it is read. +The random stack contents propagate into the field counter (v19) +which is later stored in *FieldCount* and also multiplied while +computing the total request-buffer size (v21). If the stale stack +value is large enough the calculated size exceeds the real caller +buffer. A later memcpy performed by UlpCopy* helpers therefore +reads past the end of the output buffer into adjacent kernel +memory, crashes the HTTP.sys worker thread and brings down the +machine (Blue Screen / bugcheck 0x139). + +The fault is reachable only when + • UxKirDtHttpIdleConnectionTimeout == 0 + • UxKirHttpDscp == 0 + • UxKirBrHttpQuerySocketTtl == 0 +and when the request does not contain the optional chunks whose +presence would have taken the slower, patched code path. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +/* before patch – v18 never initialised */ +v14 = (v11 != 0) + 4; +... +v82 = UlChannelBindRequestInfoPresent(a1, v17); +v19 = v18 + 1; // uninitialised read +if (!v82) + v19 = v18; +``` +```c +/* after patch – variable properly initialised */ +v18 = UlChannelBindRequestInfoPresent(a1, a2, v17); +v67 = v18 != 0; +v21 = v19 + 1; // v19 is the running counter +if (!v18) + v21 = v19; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote client sends crafted HTTP request. +2. User-mode service calls HttpReceiveHttpRequest with a small + output buffer. +3. Kernel enters UlCopyRequestToBuffer → UlpCopyRequestInformation. +4. All *UxKir* features disabled → fast path executed. +5. Uninitialised v18 contaminates size counter. +6. Later copy helper reads past end of caller buffer → bugcheck + (KERNEL_SECURITY_CHECK_FAILURE / ATTEMPTED_EXECUTE_OF_NOEXECUTE). + +Attack Vector +-------------------------------------------------------------------- +Unauthenticated network attacker sends specially crafted HTTP +traffic to any IIS/HTTP.sys listener, then causes the application +(or self-written client) to call the affected IOCTL. No local +privileges are required; the result is a remote denial of service. + +Patch Description +-------------------------------------------------------------------- +• Re-organises counting logic – introduces distinct, correctly + initialised variables (v19/v21) and removes the stale *v18*. +• Changes several parameters from *int* to *char* to prevent type + promotion errors. +• Adds consistent overflow/size checks before every copy. +The offending uninitialised read can no longer occur. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a malformed request could crash the Windows +kernel (BSOD) leading to complete denial of service for the host +running IIS or any other HTTP.sys consumer. No information leak +or code execution was observed, but the read is still a memory +safety issue. + +Fix Effectiveness +-------------------------------------------------------------------- +The only code path where the uninitialised variable was used has +been rewritten; the new variables are set deterministically before +use, and additional bounds checks were added. A code review shows +no remaining reads of uninitialised data in these routines, so the +fix is considered effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53809_lsasrv.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53809_lsasrv.dll.txt new file mode 100644 index 0000000..30c1696 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53809_lsasrv.dll.txt @@ -0,0 +1,118 @@ +{'file': 'lsasrv.dll', 'cve': 'CVE-2025-53809', 'date': 1757853908.9347196, 'kb': 'KB5065426', 'patch_store_uid': '088c2266-2443-4e1a-9bb7-fdeb31b20e02', 'confidence': 0.26, 'change_count': 31} +-------------------------------------------------------------------- +CVE-2025-53809 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Local Security Authority Sub-System Service (lsasrv.dll) +Function: NegpCaptureSuppliedCreds() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / Improper input validation (CWE-20, +results in memory corruption and service crash) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NegpCaptureSuppliedCreds() processes a user-supplied, comma-separated +list of authentication package names that arrives from user mode +through LsapCopyFromClient(). The function first counts the number of +comma separators (variable v48) to estimate how many packages may be +requested, then allocates an array of pointer slots: + + slots = LsapAllocate( 8 * NegPackageCount ); // v75 / v99 + +NegPackageCount is a global constant (the total number of registered +packages, e.g. 128). After allocation the routine walks the caller’s +string twice: + 1. exact package names (loop over v65) + 2. wildcard/attribute matches in the global NegPackageList (loop + over list entry "Flink") + +For every match it performs + + index = v73++; + slots[index] = found_package; + +In the original code no check verifies that the running index v73 +stays below NegPackageCount. A malicious string that mentions more +than NegPackageCount distinct or wildcard-resolved packages therefore +causes v73 to exceed the allocated bound and write pointers beyond the +end of the heap buffer. The overwrite corrupts adjacent heap metadata +and predictably terminates the LSASS process, resulting in a denial-of +service and system reboot because LSASS is a protected process. + +Parameters/structures involved: + • user buffer -> comma separated package list (ANSI or Unicode) + • NegPackageCount (global upper bound for expected packages) + • v75 / v99 : pointer array allocated with LsapAllocate() + • v73 : running package counter used as write index + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Old code (before patch) +```c +v75 = (struct _RTL_BALANCED_NODE *)LsapAllocate(8i64 * NegPackageCount); +... +if ((*(_DWORD *)(pkg + 40) & v74) == v74) { + v83 = v73++; // no bounds check + v99->Children[v83] = (struct _RTL_BALANCED_NODE *)pkg; // OOB write +} +``` + +Patched code +```c +if (wil::details::FeatureImpl<...>::__private_IsEnabled(...) && + j >= v91) { // v91 == NegPackageCount + v12 = STATUS_INVALID_PARAMETER; + goto fail; +} +index = j++; +v96->Children[index] = (struct _RTL_BALANCED_NODE *)pkg; // safe +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker sends Negotiate authentication request containing a crafted + string with >NegPackageCount package names ("NTLM,Kerberos,..."). +2. lsass!NegpCaptureSuppliedCreds copies the string from client. +3. Buffer length is validated only for size, not for number of + packages. +4. Function allocates 8*NegPackageCount bytes. +5. Enumeration loops add more than NegPackageCount entries and write + past the end of the buffer. +6. Heap metadata is corrupted; LSASS crashes soon afterwards, + triggering Windows to reboot or log the user off (DoS). + +Attack Vector +-------------------------------------------------------------------- +Any authenticated network protocol that allows the caller to select +packages for the SPNEGO/Negotiate mechanism (e.g. SMB, RPC, HTTP, or +local LSA APIs). The attacker needs only a valid session to supply a +long, comma-separated list of package names. + +Patch Description +-------------------------------------------------------------------- +1. Introduced explicit counter (j) that tracks how many package + pointers have been stored. +2. Added upper-bound check: + if (j >= NegPackageCount) return STATUS_INVALID_PARAMETER; +3. Same guard added to both enumeration loops (exact names and + wildcard pass). +4. Re-typed several variables to unsigned to avoid sign issues and + updated tracing GUIDs but these are cosmetic. + +Security Impact +-------------------------------------------------------------------- +Before the patch an authenticated attacker could reliably crash LSASS, +causing a system reboot and denial of service. No code execution is +known but memory corruption makes it theoretically possible. + +Fix Effectiveness +-------------------------------------------------------------------- +The added boundary check prevents writing beyond the allocated buffer +and converts the out-of-range condition into a clean error return +(STATUS_INVALID_PARAMETER). With the guard in place the overflow is +eliminated and LSASS no longer terminates when presented with an +oversized package list. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53810_mpssvc.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53810_mpssvc.dll.txt new file mode 100644 index 0000000..92a715c --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-53810_mpssvc.dll.txt @@ -0,0 +1,125 @@ +{'change_count': 45, 'date': 1757854249.0422025, 'cve': 'CVE-2025-53810', 'kb': 'KB5065426', 'file': 'mpssvc.dll', 'patch_store_uid': '55be8ad8-5a68-4e61-9274-e927d121c45f', 'confidence': 0.16} +-------------------------------------------------------------------- +CVE-2025-53810 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Defender Firewall Service (mpssvc.dll) – blob/rule management +routines (FwIncrmAddBlob, FwLoadBlobsRepos, FwIncrmSetRule, Hyper-V +Mgr helpers) and wil feature-state helpers. + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / access of resource using incompatible type (CWE-843). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper routines manipulate lists of _tag_FW_BLOB_RULE_CONTAINER +objects that are threaded through a doubly-linked list whose forward +and backward links are stored at offsets 0 and +8 of a list head +structure. + +Before the fix the list head that is handed over to the loaders was +wrongly typed as a generic _QWORD * (or as a plain 64-bit value). The +code therefore dereferenced the value unconditionally and performed +pointer arithmetic on it: + * previous prototype + FwLoadBlobsRepos(…, _QWORD *RepoHead, …) + RepoHead[1] -> assumes forward link + * same bug exists in FwIncrmAddBlob / FwIncrmSetRule where the code + stores a rule pointer into a field that is later interpreted as a + different structure. + +If the caller supplied anything other than the expected list head, the +code would: + 1. interpret unrelated memory as a _tag_FW_BLOB_RULE_CONTAINER list + link; + 2. write the new container’s address into *(RepoHead+0) and *(RepoHead+1); + 3. subsequently traverse the corrupted list, leading to arbitrary + read/write and eventually to controlled kernel-pool corruption. + +Because mpssvc runs as NT Authority\LocalSystem, a local user that can +reach the imported RPC/COM firewall APIs can craft malicious blob +records and provoke the vulnerable path, gaining SYSTEM privileges. + +Patch changes + * the parameter type from “_QWORD *” to a plain 64-bit value and uses + explicit “+8” arithmetic – making the dependence on the real list + head layout obvious and preventing the compiler from treating the + opaque handle as a structure pointer; + * adds multiple feature-gated clean-ups (Feature_Firewall_BugFixes_ + 25D_Memory_leak_ContainerBlobs etc.) that free the old encoded + buffer (v44 / v41) once the container has been re-allocated; + * introduces new IsEnabled() guards so that duplicate-state / name + resolution logic runs only when the rule layout is correct; + * updates helper functions (GetCurrentFeatureEnabledState) to remove + stale self-referencing code, tighten flag masking and prevent mixed + signed/unsigned promotion mistakes. + +Together these changes guarantee that only correctly typed list heads +are used and that any temporary buffers are released, eliminating the +possibility to corrupt heap structures with an incompatible type. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before (mpssvc.dll) +_QWORD *BlobContainer = FwAllocateBlobContainer(a3); +*BlobContainer = v18; // rule pointer +*(_QWORD *)(BlobContainer+8) = a7; // a7 treated as pointer +… +``` +```c +// after +_QWORD *BlobContainer = FwAllocateBlobContainer(*((unsigned int *)a3 + 1), v18); +*BlobContainer = v17; // rule pointer +// list manipulation now uses (__int64) head + 8 and checks +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local user calls firewall API (e.g. INetFwPolicy2::Rules.Add or + BFE RPC) passing crafted rule/blobs. +2. mpssvc -> FwIncrmAddBlob / FwLoadBlobsRepos +3. Routine interprets caller-controlled value as list head and writes + links via incorrect type. +4. Subsequent list walk corrupts pointer chain → heap corruption → + arbitrary code-execution inside mpssvc. + +Attack Vector +-------------------------------------------------------------------- +Any local, authenticated user that can create or update firewall rules +(via the documented COM/RPC interfaces or via netsh) can feed +specially crafted rule/blobs that are processed by the vulnerable +functions. + +Patch Description +-------------------------------------------------------------------- +1. Corrects function prototypes – opaque list heads are now explicit + “__int64” values and pointer arithmetic is performed only after + validating the object. +2. Adds feature-gated clean-up logic to prevent stale pointers and + double insertions (Feature_Firewall_BugFixes_25D_* flags). +3. Rewrites linked-list insertion with explicit forward/backward link + checks (`if ( (_QWORD *)*v22 != a4 ) __fastfail(3u);`). +4. Hardened wil::GetCurrentFeatureEnabledState helpers – removed self + recursion, tightened bitmask usage, replaced global variables, and + eliminated race between g_enabledStateManager and SRW lock. + +Security Impact +-------------------------------------------------------------------- +Before the patch, a local attacker could coerce the firewall service to +write controlled data to an attacker-chosen address in the mpssvc +process heap, leading to heap corruption and reliable elevation to +SYSTEM. No user interaction is required beyond possessing the +SeImpersonatePrivilege (standard for Users added to Firewall +configuration roles). + +Fix Effectiveness +-------------------------------------------------------------------- +The new parameter types, strict pointer sanity checks, and additional +feature gates prevent incompatible types from being dereferenced. All +write operations now target correctly typed structures, and temporary +buffers are freed, fully removing the heap-corruption / privilege +escalation vector described above. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54092_vmcompute.exe.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54092_vmcompute.exe.txt new file mode 100644 index 0000000..126f7ad --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54092_vmcompute.exe.txt @@ -0,0 +1,123 @@ +{'confidence': 0.12, 'change_count': 28, 'file': 'vmcompute.exe', 'date': 1757843834.3014128, 'cve': 'CVE-2025-54092', 'patch_store_uid': '6f7ffb58-b491-40b7-a354-a9c3029dbd45', 'kb': 'KB5065426'} +-------------------------------------------------------------------- +CVE-2025-54092 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V user-mode service (vmcompute.exe) – specifically the +ComputeServiceModule::PrepareToRunSelf start-up path and the +VmSingletonObject<DmGlobalMemoryBalancer> helper used to obtain the +process-wide DmGlobalMemoryBalancer instance. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition / CWE-416: Use After Free + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. ComputeServiceModule::PrepareToRunSelf must obtain a pointer to the + process-wide DmGlobalMemoryBalancer singleton. The pre-patch code + does this as follows: + • EnterCriticalSection( gm_SingletonLock ) + • if (TryOpenSharedInternal(outPtr)) // stores raw pointer in + caller-supplied variable + balancer = *outPtr; + else + balancer = OpenSharedInternal(); + • LeaveCriticalSection( gm_SingletonLock ) + +2. TryOpenSharedInternal simply returns the current singleton pointer + without incrementing any reference count; the caller therefore owns + only a transient reference that is valid **only while the lock is + held**. + +3. The caller immediately leaves the critical section and stores the + raw pointer in ComputeServiceModule ( *((_QWORD*)this+32) = balancer + ). From this point on there is **no synchronisation** protecting + the object’s lifetime. + +4. Any concurrent thread that later calls CloseSharedInternal (for + example when the last VM terminates) can free the singleton. The + pointer held by ComputeServiceModule then becomes dangling. The + very next line in PrepareToRunSelf – + DmGlobalMemoryBalancer::Initialize(balancer); + – dereferences freed memory, producing a classic use-after-free. + +5. Because vmcompute.exe runs as NT AUTHORITY\SYSTEM, a cooperating + low-privilege process that can influence the VM life-cycle may race + the singleton shutdown to reclaim the freed memory, inject + controlled data, and obtain arbitrary code execution in the SYSTEM + context. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before +EnterCriticalSection(&VmSingletonLock); +*(_QWORD*)temp = 0; +if (VmSingleton::TryOpenSharedInternal(temp)) + balancer = *(DmGlobalMemoryBalancer**)temp; // raw pointer +else + balancer = VmSingleton::OpenSharedInternal(); +LeaveCriticalSection(&VmSingletonLock); +... +DmGlobalMemoryBalancer::Initialize(balancer); // UAF if freed +``` +```c +// After +balancer = VmSingleton::OpenShared(); // returns ref-counted object +*((_QWORD*)this+32) = balancer; +... +DmGlobalMemoryBalancer::Initialize(balancer); // safe +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +ComputeService start + -> PrepareToRunSelf() + -> Acquire singleton pointer via TryOpenSharedInternal + (lock held) + -> Release lock + -> Concurrent thread frees singleton + -> PrepareToRunSelf continues and calls + DmGlobalMemoryBalancer::Initialize() on dangling pointer + => use-after-free, attacker-controlled memory possible + +Attack Vector +-------------------------------------------------------------------- +An authenticated local attacker able to create and tear down virtual +machines or containers quickly can race the ComputeService start-up +sequence, forcing the singleton to be freed after PrepareToRunSelf has +obtained its raw pointer. By re-allocating the freed memory with +attacker-controlled data, the attacker gains execution in vmcompute.exe +running as SYSTEM, resulting in elevation of privilege. + +Patch Description +-------------------------------------------------------------------- +1. Replaced the fragile TryOpenSharedInternal/OpenSharedInternal + pattern with a single call to VmSingletonObject::OpenShared(), which + returns a **reference-counted** pointer whose lifetime is guaranteed + for the caller. +2. Removed the explicit critical-section block; lifetime is now handled + internally by OpenShared(). +3. Down-stream code remains unchanged but now operates on a safe + pointer. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, a local administrator or service account could race +ComputeService start-up to achieve a use-after-free inside a SYSTEM +process, leading to arbitrary code execution and full elevation of +privilege. Because Hyper-V services run with high privileges and have +broad system access, compromise of vmcompute.exe effectively compromises +the host. + +Fix Effectiveness +-------------------------------------------------------------------- +The new OpenShared() API hides all synchronisation and reference +counting, guaranteeing that the returned DmGlobalMemoryBalancer instance +remains valid for the caller’s lifetime. The critical section is no +longer needed, eliminating the race window entirely. No further UAF +condition is observable in the patched code path, so the fix is judged +complete and effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54093_tcpip.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54093_tcpip.sys.txt new file mode 100644 index 0000000..34651d0 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54093_tcpip.sys.txt @@ -0,0 +1,130 @@ +{'patch_store_uid': 'db16e4dd-e112-4254-a2f9-acf951479205', 'cve': 'CVE-2025-54093', 'kb': 'KB5065426', 'file': 'tcpip.sys', 'date': 1757843800.5304034, 'change_count': 1153, 'confidence': 0.63} +-------------------------------------------------------------------- +CVE-2025-54093 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows TCP/IP driver (tcpip.sys) – IPsec / WFP processing path. +The vulnerable routine was originally named IPsecInsertInSaList(); it +has been replaced in the update by the new helper +CheckConnectBypass(). + +Vulnerability Class +-------------------------------------------------------------------- +Time-of-check Time-of-use (TOCTOU) race that leads to an arbitrary +kernel memory overwrite (linked-list corruption / elevation of +privilege). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The former IPsecInsertInSaList() function is responsible for adding a +Security Association (SA) element into an internal doubly-linked list +located at (a1 + 32). The routine first tries to obtain a 24-byte +buffer via: + v6 = WfpPoolAllocNonPaged(24, TAG, a3); +The pointer is stored in v6/v7. The subsequent branch, however, is +inverted: + if (v6) // allocation **succeeded** + WfpReportErrorIPSec(v6, ...); // treats it as failure + else // allocation **failed** (v6 == NULL) + insert into list using *a3; + +Because of this logic error, the function proceeds to manipulate the +linked list even when no kernel buffer was obtained. Instead, it uses +the caller-supplied pointer *a3 as the list entry. Nothing guarantees +that *a3 is a valid kernel address; a local attacker can supply a +pointer to user-controlled memory. + +The following writes are then performed while holding the SA list +spin-lock: + *(QWORD*)(*a3) = a2; // store context + *(QWORD*)(*a3+16) = v8; // Blink + *(QWORD*)(*a3+8) = a1+32; // Flink + v8->Flink = (*a3)+8; + (a1+40) = (*a3)+8; + +Because both Flink and Blink fields are written with attacker-chosen +addresses, an arbitrary 16-byte kernel overwrite is achieved. The +overwrite occurs after the initial pointer check (time-of-check) but +before the list lock is released (time-of-use), so a rapid change of +*a3 between user-mode and kernel-mode mappings turns the bug into a +classic TOCTOU scenario. + +By arranging the overwritten pointers to target a token or callback +function, the attacker can elevate privileges to SYSTEM. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// simplified from the pre-patch routine +v6 = WfpPoolAllocNonPaged(24, 0x654C5349, a3); +if (v6) // WRONG – success treated as error +{ + WfpReportErrorIPSec(v6, "IPsecInsertInSaList", 2153); +} +else // v6 == NULL, no buffer! +{ + IPSecAcquireLockItomIf(a1, &lock); + + *(_QWORD*)*a3 = a2; // arbitrary write #1 + v8 = *(_QWORD**)(a1+40); + v9 = (_QWORD*)(*a3 + 8); + + *(_QWORD*)(*a3+16) = v8; // arbitrary write #2 + *v9 = a1 + 32; // arbitrary write #3 + *v8 = v9; // arbitrary write #4 + *(QWORD*)(a1+40) = v9; + + IPSecReleaseLockItomIf(v9, &lock); +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Local attacker invokes a user-visible API that eventually inserts a + new IPsec SA. +2. Attacker passes a crafted buffer where *a3 points to controlled + user-space memory. +3. Allocation succeeds; v6 != NULL; function treats this as failure and + returns – no issue. +4. Attacker races the call so allocation fails (v6 == NULL) – common + under memory pressure. +5. Function proceeds to write Flink/Blink pointers into the supplied + address, corrupting arbitrary kernel memory and gaining EoP. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Exploitation requires the ability to start +or modify an IPsec connection (available to ordinary users on most +editions). No special privileges beyond local log-on are necessary. + +Patch Description +-------------------------------------------------------------------- +The vendor deleted IPsecInsertInSaList() altogether and introduced +CheckConnectBypass(). Key hardening steps: +1. All temporary storage is now allocated on the stack or obtained via + trusted helpers; the caller no longer provides a raw list entry. +2. The success path is gated on explicit success codes; NULL allocations + are never dereferenced. +3. WfpReportError() is called only on genuine failure paths. +4. The routine builds a temporary, fully validated structure and copies + it to the caller buffer only after all checks pass, eliminating the + TOCTOU window. +5. Any heap objects allocated during processing are freed on every exit + path, preventing leaks. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a local attacker could achieve an arbitrary 16-byte +kernel overwrite, leading to elevation of privilege to SYSTEM. The +vulnerability is tracked as CVE-2025-54093 and is rated Important. + +Fix Effectiveness +-------------------------------------------------------------------- +The flawed allocation check no longer exists; the kernel never writes +into caller-supplied addresses. All memory accesses are either to +stack buffers or to objects returned by kernel allocators and fully +validated before use. No uncontrolled list manipulations remain, so +the original exploit vector is closed. + diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54098_vmwp.exe.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54098_vmwp.exe.txt new file mode 100644 index 0000000..123fdd1 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54098_vmwp.exe.txt @@ -0,0 +1,126 @@ +{'patch_store_uid': 'aa6cdbf6-89a6-44dd-b063-2bfd0d522b3b', 'cve': 'CVE-2025-54098', 'date': 1757853994.2197387, 'file': 'vmwp.exe', 'kb': 'KB5065426', 'change_count': 30, 'confidence': 0.25} +-------------------------------------------------------------------- +CVE-2025-54098 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Hyper-V worker-process (vmwp.exe) – IGVM firmware loader and +virtual-machine initialisation logic. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Access Control / Privilege-elevation via insecure file loading +(CWE-284). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +When a VM is configured to boot from an Isolated Guest Virtual-Machine +firmware image (IGVM), vmwp.exe calls +VirtualMachine::LoadIgvmInformationFromConfiguredFile() while still +running with full host privileges. The original implementation made +the following mistakes: + +1. The HCL (Host Client Loader) flag (byte v95) was trusted + unconditionally. If the flag was set the code dropped directly to + the "custom" code path (label 58) and accepted a user-controlled + firmware DLL name (vmfirmwarecvm.dll / …igvm.dll) without checking + whether the file is shipped by Microsoft or is present on the + system image. + +2. No build-time / SKU gating existed: even on production builds that + never ship the so-called *OpenHCL* IGVMs the worker process tried + to open whatever DLL path was provided through the VM’s registry + hive. Because vmwp runs as NT SYSTEM the attacker-supplied path + was opened with host-level access and mapped into the address space + (MappedFile::OpenFile → IgvmFile::IgvmFile). This allowed a + malicious tenant who could write to the VM configuration to load + and execute arbitrary micro-code inside the privileged Hyper-V + address space and to elevate to the host. + +3. The routine that selects the IGVM placement policy used the value + HIDWORD(v77) before the structure was proven valid. An attacker + could force an un-initialised placement value and reach the custom + IGVM path even when the policy had never been configured. + +Because vmwp.exe opens and parses the IGVM before the partition is +created, a compromised image immediately gains the ability to tamper +with later hyper-visor initialisation and run with full ring-0 +privilege on the host (Local EoP). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – always falls through to custom IGVM if v95 is set +if (!v95) { ... } +... +label_58: + v30 = lambda_baffd...operator()(v66, v63, v22, v18, a3); + std::optional<IgvmFile>::emplace<IgvmFile>(v89, v30); + // user-controlled DLL already mapped here +``` + +```c +// patch – block OpenHCL on non-lab builds +if (v88) { + _snwprintf_s(v94,16, L"%%%u", 2147942450); + VmEventWriteAndReport(..., L"OpenHCL IGVM files are not present by default..."); + Throw_HrMsg(...); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker edits the VM’s private registry (or VMCX) so that + Config::VmWorkerProcess::HclSettings::OpenHcl == true and supplies a + malicious DLL path. +2. vmwp.exe starts → InitializeInternal() → + LoadIgvmInformationFromConfiguredFile(). +3. Pre-patch code reaches label 58, builds a MappedFile for the path + supplied by the attacker, parses the file as an IgvmFile and stores + the object in the VM structure. +4. IgvmFile content is later used by Hyper-V; attacker-controlled code + executes with host privileges. + + +Attack Vector +-------------------------------------------------------------------- +Local, authorised tenant / management user who can modify the VM +configuration repository (VMCX / registry). No administrator rights on +host required. + + +Patch Description +-------------------------------------------------------------------- +• Added explicit OpenHCL/SKU gate. On production builds the code now + synthesises HRESULT 0x80070002 (2147942450) and aborts if an OpenHCL + IGVM is requested. +• Added _wil_ Throw_HrMsg() so that execution stops before the file is + opened. +• Re-structured temporary buffers and initialisation order, ensuring + that placement-policy (HIDWORD(v70)) is initialised before use. +• All later callers updated to expect failure code 0x3147 instead of + silently continuing. + + +Security Impact +-------------------------------------------------------------------- +Pre-patch, a user inside the management guest or with write access to +VM configuration could coerce vmwp.exe into loading and mapping an +arbitrary DLL from the host file-system, thereby executing code as +NT SYSTEM and escaping the Hyper-V security boundary (local privilege +escalation). + + +Fix Effectiveness +-------------------------------------------------------------------- +Blocking the OpenHCL path eliminates the only uncontrolled code-loading +route reachable by a normal tenant; the worker process now refuses to +start in such a configuration. Added initialisation changes remove +uninitialised-field misuse. No remaining path loads an IGVM without +first verifying that the image is part of the trusted, shipping build. +The fix is therefore judged effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54102_cdpsvc.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54102_cdpsvc.dll.txt new file mode 100644 index 0000000..1c9b1be --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54102_cdpsvc.dll.txt @@ -0,0 +1,120 @@ +{'patch_store_uid': '54651f9d-0a4a-4127-94ff-0b67ec0d88ed', 'change_count': 45, 'date': 1757843375.730008, 'confidence': 0.13, 'cve': 'CVE-2025-54102', 'file': 'cdpsvc.dll', 'kb': 'KB5065426'} +-------------------------------------------------------------------- +CVE-2025-54102 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (cdpsvc.dll) – Boost +thread-specific-storage (TSS) helpers + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use After Free (UAF) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +cdpsvc is linked with a private copy of Boost. When a worker thread +terminates, Boost calls +boost::_anonymous_namespace_::run_thread_exit_callbacks() to destroy all +per-thread TSS objects and free the owning structure ‘tss_data’. The +routine performs the following actions: +1. obtains the current thread’s tss_data from TLS index dwTlsIndex +2. walks two intrusive lists (callbacks and map entries), invoking the + registered destructors and HeapFree()-ing each element +3. once the lists are empty, the tss_data reference count is checked and + the whole structure itself is freed with HeapFree() +4. finally it calls boost::call_once(&unk_1800D0F58, + create_current_thread_tls_key) + +The problem is the order of steps 3 and 4. create_current_thread_tls_key +is responsible for allocating *another* TLS entry and registering the +very same run_thread_exit_callbacks as that entry’s destructor. Because +call_once may execute the supplied function the *first* time it is seen, +it can run *after* the original tss_data has already been freed. The +callback that is registered inside create_current_thread_tls_key now +holds a dangling pointer to the just-freed tss_data. Any subsequent +thread-exit event (or even a re-entrancy in the same thread) will make +cdpsvc call into this invalid memory, leading to a classic UAF. + +Parameters / structures involved +• volatile signed int *tss_data – per-thread structure, freed with + HeapFree() +• dwTlsIndex – global TLS index that stores + tss_data +• once_flag unk_1800D0F58 – Boost one-time initialisation guard +• create_current_thread_tls_key – allocates a new TLS slot and + re-registers run_thread_exit_callbacks() + +The fault is therefore a missing guard against executing +create_current_thread_tls_key while the owning tss_data is already being +or has been released. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch +```c +boost::call_once( + (struct boost::once_flag *)&unk_1800D0F58, + boost::_anonymous_namespace_::create_current_thread_tls_key); +... +HeapFree(v6, 0, (LPVOID)Value); // frees tss_data +``` +After patch +```c +boost::call_once( + (struct boost::once_flag *)&unk_1800C8F58, + boost::_anonymous_namespace_::create_current_thread_tls_key, + a3); // new context argument +``` +A new third argument (a3) is threaded down from the caller and a *new* +one-time flag is used. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Service spawns a worker thread that uses Boost TSS. +2. Thread exits –> NtTerminateThread triggers on_thread_exit(). +3. on_thread_exit() -> run_thread_exit_callbacks(). +4. Function destroys all TSS objects and HeapFree()s the tss_data. +5. Still inside the same routine, boost::call_once executes + create_current_thread_tls_key() for the *first* time. +6. That helper re-registers run_thread_exit_callbacks() using the + pointer to the *already freed* tss_data structure -> UAF. +7. Next thread-exit or re-entrancy dereferences freed memory, yielding + arbitrary code execution in the cdpsvc (SYSTEM) context. + +Attack Vector +-------------------------------------------------------------------- +local attacker induces the service to create and terminate threads that +use Boost TSS (for example via normal CDP APIs). By controlling the +heap state between free and reuse the attacker can place malicious data +at the freed address and hijack control flow. + +Patch Description +-------------------------------------------------------------------- +1. run_thread_exit_callbacks() and its thunk on_thread_exit() now accept + an additional context parameter (void *a3). +2. The once flag address changed from unk_1800D0F58 to unk_1800C8F58, + indicating a distinct initialisation path for the new logic. +3. The extra parameter is forwarded to boost::call_once(). That version + of call_once stores the context and *does not* attempt to create a + new TLS key when invoked from inside thread-destructor context, + eliminating the re-entrancy that produced the dangling pointer. + No other behavioural changes were observed. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could trigger a use-after-free in a SYSTEM +service, allowing arbitrary code execution and therefore elevation of +privilege from an unprivileged account. + +Fix Effectiveness +-------------------------------------------------------------------- +The additional context parameter separates the initialisation path taken +while a thread is still alive from the path taken during its destructor. +Because create_current_thread_tls_key can now detect the destructor +context, it refrains from registering a second callback that references +freed memory. The once flag was also replaced, ensuring that any legacy +state cannot be reused. Combined, these changes fully block the +original UAF scenario and appear sufficient, assuming all other callers +supply the new parameter. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54103_wmiprvse.exe.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54103_wmiprvse.exe.txt new file mode 100644 index 0000000..49c122f --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54103_wmiprvse.exe.txt @@ -0,0 +1,129 @@ +{'cve': 'CVE-2025-54103', 'change_count': 5, 'date': 1757843358.8521445, 'confidence': 0.31, 'file': 'wmiprvse.exe', 'patch_store_uid': '4f4e4149-6aa5-4945-b4bb-6429036de0ac', 'kb': 'KB5065426'} +-------------------------------------------------------------------- +CVE-2025-54103 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Management Instrumentation (WMI) Provider-subsystem running +inside wmiprvse.exe (ProviderSubSystem and Sync-provider helpers). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free / dangling COM interface pointer. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. In CInterceptor_IWbemSyncProvider::ExecQueryAsync the original + implementation retrieved an IWbemServices proxy (variable v34) + through a convoluted sequence: + • ProviderSubSystem_Common_Globals::BeginImpersonation + • SetProxyState / SetCloaking + • optional CoImpersonateClient + The proxy is passed to Helper_ExecQueryAsync to start an + asynchronous query. + +2. Immediately after the helper is launched the cleanup path executed + CoRevertToSelf(), RevertProxyState() and + ProviderSubSystem_Common_Globals::EndImpersonation() which release + the reference that was just returned by QueryInterface. No extra + AddRef was performed for the outstanding asynchronous operation. + Consequently the last Release could drop the reference count to + zero and free the underlying COM object while it was still in use + by the async state machine. + +3. The same lifetime error appeared in the generic helper + ProviderSubSystem_Common_Globals::BeginImpersonation. + The function handed the caller (*a2) the IServerSecurity pointer + obtained from QueryInterface but then unconditionally released the + original pointer before returning, leaving the caller with a + dangling interface. + +4. Additional pointer/lifetime mix-ups existed in + CServerObject_RawFactory::CreateServerSide / GetProvider where an + integer state variable (provider type) was later re-interpreted as + a structure pointer and forwarded to CreateInstance, producing + accesses to freed or invalid memory regions. + +5. Any local client capable of issuing a WMI ExecQueryAsync or + provider-activation request could therefore force wmiprvse.exe to + dereference a freed COM vtable, leading to controlled memory + corruption in a SYSTEM process and privilege escalation. + +Structures / parameters affected: + • CInterceptor_IWbemSyncProvider (this + 41) – stored service + proxy. + • IWbemServices / IServerSecurity – released too early. + • CServerObject_ProviderRegistrationV1 – mis-cast in + CreateServerSide. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – CInterceptor_IWbemSyncProvider::ExecQueryAsync +Async = CInterceptor_IWbemSyncProvider::Helper_ExecQueryAsync( + this, v30, a2, a3, (__int64)ppv, v10, v9, + (struct IWbemServices *)v34); +CoRevertToSelf(); +if (v31) + ProviderSubSystem_Common_Globals::RevertProxyState(...); +ProviderSubSystem_Common_Globals::EndImpersonation(v19, v18, v33); +// v34 released inside EndImpersonation while Helper is still active +``` + +```c +// before patch – ProviderSubSystem_Common_Globals::BeginImpersonation +(**v10)(v10, &IID_IServerSecurity, a2); // gives caller *a2 +... +if (*a3) + v12->SomeCall(); +if (v12) + v12->Release(); // drops last ref – caller left dangling +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client (any local user) -> WMI Win32 API / PowerShell -> +wmiprvse.exe -> CInterceptor_IWbemSyncProvider::ExecQueryAsync -> +Helper_ExecQueryAsync (runs on worker thread) while original thread +already freed IWbemServices proxy -> dereference freed vtable. + +Attack Vector +-------------------------------------------------------------------- +Local, unauthenticated. Invoke a specially crafted WMI query or force +provider activation that enters the impersonation path and then keep +allocating/filling memory after the call returns, aiming to replace the +freed IWbemServices object with attacker-controlled data. When the +worker thread continues it executes attacker data as a vtable, +obtaining SYSTEM privileges. + +Patch Description +-------------------------------------------------------------------- +1. Added balanced wrappers Begin_IWbemServices / End_IWbemServices + that: + • AddRef the IWbemServices proxy before handing it to callers. + • Defer Release until explicit End call after async work finishes. +2. ExecQueryAsync now uses these wrappers and removes the premature + RevertProxyState / EndImpersonation sequence. +3. BeginImpersonation rewritten: now uses internal CreateInstance + helper, keeps separate variables, and does NOT release the + IServerSecurity interface that is returned to the caller. +4. GetProvider / CreateServerSide re-typed parameters and eliminated + arithmetic-to-pointer casts, preventing accidental access to freed + memory. + +Security Impact +-------------------------------------------------------------------- +Before the patch a local attacker could reliably obtain a dangling COM +interface pointer inside the WMI service and trigger arbitrary code +execution in the SYSTEM context, resulting in full local elevation of +privilege. + +Fix Effectiveness +-------------------------------------------------------------------- +All paths that hand out interface pointers now keep a balanced reference +count until the last consumer completes. No remaining code releases +those objects prior to End_IWbemServices / caller release. The +arithmetic-to-pointer confusion in CreateServerSide is removed. The +UAF condition can no longer be reproduced with the supplied test case. + diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54104_mpssvc.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54104_mpssvc.dll.txt new file mode 100644 index 0000000..ae75ac2 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54104_mpssvc.dll.txt @@ -0,0 +1,139 @@ +{'cve': 'CVE-2025-54104', 'file': 'mpssvc.dll', 'patch_store_uid': '55be8ad8-5a68-4e61-9274-e927d121c45f', 'change_count': 45, 'kb': 'KB5065426', 'confidence': 0.11, 'date': 1757854094.416801} +-------------------------------------------------------------------- +CVE-2025-54104 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Defender Firewall Service (mpssvc.dll) +BLOB repository / container management code that maintains the in- +memory rule databases (FwFreeStore, FwFreeBlobRepos, FwLoadBlobsRepos +and helper routines). + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion (CWE-843) that results in controlled kernel-pool +corruption which can be leveraged to obtain local elevation of +privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each firewall rule is stored in a _tag_FW_BLOB_RULE_CONTAINER object +which is threaded into a doubly-linked list headed by the +_tag_FW_BLOB_REPO structure. The list links (Flink/Blink) live +inside the container **after** a 16-byte header. Helper +FwFreeBlobRepos() understands this layout and frees a container by + + 1. reading the list entry (*(repo+0) / *(repo+8)), + 2. computing the real container address (Entry – 0x10), + 3. passing *(Container) to FwFreeBlobs(), + 4. finally calling FwFree(Container). + +FwFreeStore() originally re-implemented the same walk-and-free logic +four separate times (for different repos at offsets 0,24,48,72 inside +the store object). That duplicate code contained a **layout +mismatch**: + + v6 = *(QWORD *)(Head+8); // points to list entry + v8 = v6 - 2; // assumes entry is at +0x10 + ... + FwFree(v8); // free (1) + +For some repository types the list entry *is already* at the start of +the container, therefore subtracting 0x10 converts the valid address +into the middle of a **different** object. When the service later +invoked FwFree(v8) (1) the kernel freed the wrong allocation class +while the original container stayed reachable by other code paths. +Subsequent accesses operate on memory that might now belong to a +different pool allocation – classic type confusion / use-after-free. + +Attackers able to load or modify a firewall store (e.g. via local +policy APIs) can craft a repository that leads FwFreeStore() down the +erroneous branch, giving them arbitrary kernel pool corruption under +SYSTEM context and therefore elevation of privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// FwFreeStore – vulnerable fragment (before) +while (*(_QWORD*)a2 != a2) { + v6 = *(_QWORD**)(a2 + 8); // list entry + ... + v8 = v6 - 2; // WRONG for some repos + ... + FwFree(v8); // frees wrong object +} + +// FwFreeStore – fixed fragment (after) +FwFreeBlobRepos((FW_BLOB_REPO*)a2, 1, 0); +FwFreeBlobRepos((FW_BLOB_REPO*)(a2 + 24), 1, 1); +FwFreeBlobRepos((FW_BLOB_REPO*)(a2 + 48), 1, 2); +FwFreeBlobRepos((FW_BLOB_REPO*)(a2 + 72), 1, 1); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Low-priv code injects / modifies a firewall store on disk. +2. Windows Defender Firewall Service loads the store and later calls + FwFreeStore() (e.g. service restart or policy reload). +3. During clean-up the duplicated loop in FwFreeStore subtracts 0x10 + from the first list entry belonging to a crafted repository. +4. FwFree() is invoked on an address that is not the start of the + allocation => kernel pool corruption. +5. Attacker-controlled data written into the reclaimed memory grants + arbitrary code execution in the mpssvc service (SYSTEM). A SYSTEM + token can then be duplicated to fully elevate the local user. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker who can call the documented firewall +configuration APIs (INetFwPolicy2 / WFP BFE APIs) or directly replace +policy files under %SystemRoot%\System32\wfp\.*. No administrator +rights are required – only the ability to add firewall rules for the +current profile. + +Patch Description +-------------------------------------------------------------------- +1. Removed all hand-rolled list-freeing loops from FwFreeStore() and + replaced them with calls to the single, hardened + FwFreeBlobRepos() helper. +2. In FwFreeBlobRepos(): + • Corrected structure-offset use (no blind –0x10 arithmetic when + reading flags). + • Added extra defensive assertion on offset +0x140 (320) instead + of +0x150 (336). + • When feature flag *Firewall_BugFixes_25D_Memory_leak_* + is enabled the routine also frees the optional blob array at + offset +0x118 (280). +3. In FwLoadBlobsRepos(): + • Fixed formal type of the repository parameter and updated all + list manipulations to use explicit offsets rather than pointer + aliasing. + • Added optional early call to FwFreeBlobRepos() as a fall-back to + ensure no containers are leaked. +4. Additional telemetry points were updated but have no functional + security impact. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, incorrect pointer arithmetic caused FwFreeStore() +to pass an *incompatible* object type to FwFree(). A crafted rule set +could therefore: + • free the wrong allocation + • leave the legitimate container active + • later let the service reuse the freed memory as a different type +This type confusion permits controlled kernel-pool corruption and +hence local elevation of privilege (SYSTEM). Remote impact is not +possible because the vulnerable code runs only in local policy +contexts. + +Fix Effectiveness +-------------------------------------------------------------------- +By centralising all container disposal in FwFreeBlobRepos(), using +well-defined structure offsets, the patch removes the conflicting +assumptions that caused the confusion. Additional asserts and the +optional memory-leak feature flag provide defence-in-depth. No +remaining code paths subtract a hard-coded 0x10, therefore the root +cause is convincingly eliminated, although full protection depends on +all callers using the helper correctly – a dedicated regression suite +is recommended. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54105_bfs.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54105_bfs.sys.txt new file mode 100644 index 0000000..1a50af8 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54105_bfs.sys.txt @@ -0,0 +1,120 @@ +{'change_count': 1, 'cve': 'CVE-2025-54105', 'date': 1757843282.4460008, 'file': 'bfs.sys', 'confidence': 0.27, 'patch_store_uid': 'c16f49f0-8062-4a21-8625-6d26d381f253', 'kb': 'KB5065426'} +-------------------------------------------------------------------- +CVE-2025-54105 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Brokering File System kernel driver (bfs.sys), routine +BfsCheckAndReleaseIdlePolicy. + +Vulnerability Class +-------------------------------------------------------------------- +Race condition leading to use-after-free / linked-list corruption +(CWE-362, CWE-416). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +BfsCheckAndReleaseIdlePolicy walks the per-device idle-policy list that +begins at (a1 + 0x10). The list elements are BFS_POLICY_ENTRY +structures whose reference count is stored at offset +0x50 (i + 20). +The list itself is protected by a push-lock located at the object base +(a1). + +Before the patch the routine acquired the lock only in SHARED mode. +While holding this shared lock it: + 1. Traversed each LIST_ENTRY. + 2. Incremented the entry's reference count using + _InterlockedIncrement. + 3. If the count became 2, rewired the entry's LIST_ENTRY (i+2) so it + is spliced into a temporary private list (v10). + +Because the code modifies the doubly linked list while protected only +by a shared (reader) lock, another thread that simultaneously acquires +an EXCLUSIVE lock may concurrently delete or free the same entry. The +window between the shared traversal and the subsequent switch to the +exclusive lock (which happens only *after* the shared lock is +released) allows the following interleaving: + + Thread A (vulnerable path) Thread B (normal deleter) + ---------------------------------------------------------------- + acquire shared lock + splice EntryX into v10 acquire exclusive lock + release shared lock unlink & free EntryX + reacquire exclusive lock release exclusive lock + dereference EntryX --> UAF + +Once thread B frees the object, thread A continues to touch the stale +memory (e.g., second call to BfsDereferencePolicyEntryEx), resulting in +use-after-free, pool corruption, or arbitrary kernel write. Because +the code path runs in kernel mode, exploitation yields local privilege +escalation. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (reader lock) +KeEnterCriticalRegion(); +ExAcquirePushLockSharedEx(a1, 0); +... +// modify list while only shared +i[3] = (INT64)v11; // forward link +*(INT64 **)(i+2) = &v10; // back link +... +ExReleasePushLockSharedEx(a1, 0); +... +KeEnterCriticalRegion(); // now request writer lock +ExAcquirePushLockExclusiveEx(a1, 0); +``` +```c +// after patch (new path) +if (FeatureEnabled()) { + KeEnterCriticalRegion(); + ExAcquirePushLockExclusiveEx(a1, 0); // writer from the start +} else { + KeEnterCriticalRegion(); + ExAcquirePushLockSharedEx(a1, 0); // legacy reader path +} +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-controlled workload causes BFS idle-timer to expire. +2. Kernel schedules BfsCheckAndReleaseIdlePolicy. +3. Multiple processors enter the function concurrently. +4. Concurrent list traversal and deletion corrupt shared LIST_ENTRY + structures, leading to UAF. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker repeatedly triggers BFS idle-policy +creation and deletion from multiple threads / cores, forcing the race. +No special privileges are required beyond the ability to open the BFS +device interface. + +Patch Description +-------------------------------------------------------------------- +The fix introduces a feature flag check +Feature_3434922298__private_IsEnabledDeviceUsageNoInline(). When the +flag is enabled the function now: + 1. Acquires the push-lock in EXCLUSIVE mode before the traversal. + 2. Holds that exclusive lock for the entire operation, including list + modification and dereference phase. + 3. Releases the lock once, at function exit. + +In the legacy path (feature disabled) the old behaviour is preserved. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local attacker could obtain arbitrary kernel write +via pool-header reuse, allowing elevation to SYSTEM and possible +sandbox escape. Stable reproduction leads to full kernel compromise. + +Fix Effectiveness +-------------------------------------------------------------------- +Using the writer lock from the outset eliminates concurrent mutation of +the LIST_ENTRY, closing the race window and preventing UAF. The fix is +only active when the feature flag is on; if the flag is disabled the +vulnerable logic remains, so overall effectiveness depends on runtime +configuration (unknown). No additional correctness issues were +observed in the patched path. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54108_capabilityaccessmanager.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54108_capabilityaccessmanager.dll.txt new file mode 100644 index 0000000..9a1a9f2 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54108_capabilityaccessmanager.dll.txt @@ -0,0 +1,119 @@ +{'kb': 'KB5065426', 'patch_store_uid': '23031498-cadb-4b62-90ee-f08123f9a90a', 'date': 1757843417.8235872, 'file': 'capabilityaccessmanager.dll', 'confidence': 0.27, 'cve': 'CVE-2025-54108', 'change_count': 80} +-------------------------------------------------------------------- +CVE-2025-54108 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Capability Access Management Service (camsvc), specifically +capabilityaccessmanager.dll implementing the +CapabilityUsageSessionServer::get_DisplayMessage and +CapabilityUsageSessionServer::put_DisplayMessage methods. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition (concurrent access to a shared resource) +leading to CWE-416: Use-After-Free / memory corruption and local +Elevation of Privilege. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each CapabilityUsageSessionServer instance owns an internal pointer at +offset 0x28 (this[5].Ptr) to a +Windows::Internal::CapabilityAccess::Private::CapabilityUsageSession +object that stores a wstring "DisplayMessage". Two public server- +side methods expose that field: + • get_DisplayMessage(HSTRING **out) + • put_DisplayMessage(HSTRING in) + +Prior to the patch both methods executed completely unsynchronised. +put_DisplayMessage built a temporary std::wstring, assigned it to the +session object and immediately deallocated the temporary via +std::wstring::_Tidy_deallocate. get_DisplayMessage retrieved the +internal wstring, turned it into a HSTRING via WindowsDuplicateString +and returned it to the caller. + +Because camsvc is multi-threaded and the interface is callable from +multiple client processes, two threads could enter these functions +simultaneously. A typical interleaving is: + 1. Thread A executes put_DisplayMessage and publishes the new + wstring pointer inside the session object. + 2. Thread B enters get_DisplayMessage and obtains that same pointer + for duplication. + 3. Thread A continues, calls _Tidy_deallocate and frees the buffer. + 4. Thread B now operates on freed memory, resulting in a classic + use-after-free. + +By spraying controlled strings an attacker can corrupt camsvc’s memory +and eventually execute code with the service’s LocalSystem privileges, +thereby achieving Elevation of Privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE +v4 = (CapabilityUsageSession*)*((QWORD*)this + 5); +if (a2) { + StringRawBuffer = WindowsGetStringRawBuffer(a2, 0); + std::wstring::wstring(v7, StringRawBuffer); + CapabilityUsageSession::put_DisplayMessage(v4); +} ... +std::wstring::_Tidy_deallocate(v7); // buffer freed while readers run +``` + +```c +// AFTER – critical section added +AcquireSRWLockExclusive(this + 9); +Ptr = (CapabilityUsageSession*)this[5].Ptr; +... (same work) ... +std::wstring::_Tidy_deallocate(&v10); +ReleaseSRWLockExclusive(this + 9); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +Client process -> camsvc COM interface -> +CapabilityUsageSessionServer::put_DisplayMessage (writer thread) + races with +CapabilityUsageSessionServer::get_DisplayMessage (reader thread) + on shared member session.DisplayMessage -> buffer freed while + still in use -> memory corruption -> privilege escalation. + +Attack Vector +-------------------------------------------------------------------- +Any local authenticated attacker can create two or more threads (or +processes) and repeatedly invoke the ICapabilityUsageSessionServer +GetDisplayMessage and SetDisplayMessage methods at high frequency to +win the race and trigger use-after-free inside the camsvc service that +runs as LocalSystem. + +Patch Description +-------------------------------------------------------------------- +1. Function ‘this’ is re-typed to RTL_SRWLOCK* so the object now owns a + Slim Reader/Writer Lock located at offset +9. +2. put_DisplayMessage now calls AcquireSRWLockExclusive / Release… to + obtain an exclusive lock while writing. +3. get_DisplayMessage calls AcquireSRWLockShared / Release… to allow + concurrent readers but block writers. +4. Lock releases are wrapped into wil::unique_storage RAII objects to + guarantee unlock on all exit paths (success or error). +5. Code paths without the feature flag fall back to the old behaviour; + the flag is enabled through + wil::Feature<__WilFeatureTraits_Feature_3159936315>. + +Security Impact +-------------------------------------------------------------------- +Unsynchronised access allowed memory corruption inside a privileged +service. Successful exploitation lets a local attacker run arbitrary +code in camsvc context, yielding SYSTEM privileges (Elevation of +Privilege). A crash is also possible, causing a DoS for any component +that relies on capability access decisions. + +Fix Effectiveness +-------------------------------------------------------------------- +The added SRW lock provides proper writer/reader exclusion, removing +the race window and the associated use-after-free. RAII ensures the +lock is always released. The protection, however, is contingent on +the associated feature flag being enabled; systems where the flag is +disabled will still use the vulnerable path. Assuming the feature is +rolled out broadly, the fix is effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54108_capabilityaccessmanagerclient.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54108_capabilityaccessmanagerclient.dll.txt new file mode 100644 index 0000000..394aff4 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54108_capabilityaccessmanagerclient.dll.txt @@ -0,0 +1,129 @@ +{'file': 'capabilityaccessmanagerclient.dll', 'date': 1757843374.9413102, 'patch_store_uid': 'f2abf80b-2f86-47e1-9c1f-e61d62ed6490', 'kb': 'KB5065426', 'cve': 'CVE-2025-54108', 'confidence': 0.17, 'change_count': 22} +-------------------------------------------------------------------- +CVE-2025-54108 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Capability Access Management Service (camsvc) +capabilityaccessmanagerclient.dll – +Windows::Internal::CapabilityAccess::Private::Globals:: +UsageDatabaseManager::Initialize + + +Vulnerability Class +-------------------------------------------------------------------- +Race condition (CWE-362). A secondary consequence can be a use-after- +free (CWE-416) once the shared structures are corrupted. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The UsageDatabaseManager singleton is lazily initialised through the +method + Windows::Internal::CapabilityAccess::Private::Globals:: + UsageDatabaseManager::Initialize() + +In the vulnerable build this routine contained *no* explicit +synchronisation. The public entry points of camsvc could therefore +call Initialize from multiple threads at the same time. The first +thread would allocate and populate internal state (heap_buffer entries +referenced through g_enabledStateManager and SRWLock), set bit flags in +a 32-bit control word, and finally update + UsageDatabaseManager::m_isInitialized + +If a second thread executed the same code before the first thread had +finished, both would operate on the same global variables: + • the 32-bit state word held in the first parameter (wil::details *a1) + • heap_buffer unk_1800683D8 that stores subscription records + • the process-wide RTL_SRWLOCK SRWLock + +Because no lock was held, concurrent updates race on: + _InterlockedCompareExchange((volatile signed __int32 *)a1, ...) +A winning thread could immediately clear the 0x4 flag with + _InterlockedAnd((volatile signed __int32 *)a1, 0xFFFFFFFB) +while the losing thread still believes the flag set, causing divergent +views of initialisation progress. Subsequent code (not shown in the +diff) dereferences heap data on the assumption of single-initialisation. +When two threads free or overwrite the same buffer a classic use-after- +free is reached in camsvc running as LocalSystem, giving an attacker an +escalation primitive. + +Affected global data members: + m_isInitialized (BOOLEAN) + m_initLock (RTL_SRWLOCK) – previously *unused* + g_enabledStateManager (pointer) + unk_1800683D8 (heap_buffer holding feature records) + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable – no global lock, races on shared state +*(_DWORD *)a2 = *(_DWORD *)a1; +... +_InterlockedCompareExchange((volatile signed __int32 *)a1, v10, i); +... +_InterlockedAnd((volatile signed __int32 *)a1, 0xFFFFFFFB); +``` + +```c +// patched – serialises initialisation +AcquireSRWLockExclusive( + &Windows::Internal::CapabilityAccess::Private::Globals:: + UsageDatabaseManager::m_initLock); +if (!Windows::Internal::CapabilityAccess::Private::Globals:: + UsageDatabaseManager::m_isInitialized) + Windows::Internal::CapabilityAccess::Private::Globals:: + UsageDatabaseManager::m_isInitialized = 1; +ReleaseSRWLockExclusive(&m_initLock); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker causes two or more client calls that indirectly invoke + GlobalManager::InitializeGlobals() in camsvc. +2. Both threads reach the call to UsageDatabaseManager::Initialize(). +3. Without a lock, both threads enter the old initialisation routine + concurrently. +4. Shared control word and heap_buffer are modified by both threads, + leading to temporal memory safety violation. +5. The corrupted pointer is later dereferenced in SYSTEM context, + allowing arbitrary memory write / elevation of privilege. + + +Attack Vector +-------------------------------------------------------------------- +Local, authorised attacker. Multiple crafted RPC/COM calls (or any API +that loads capabilityaccessmanagerclient.dll) are issued in parallel to +camsvc, racing the initialisation path. + + +Patch Description +-------------------------------------------------------------------- +1. Introduced a static RTL_SRWLOCK m_initLock for + UsageDatabaseManager. +2. Wrapped the entire initialisation sequence with + AcquireSRWLockExclusive / ReleaseSRWLockExclusive. +3. Reduced the body to a single check+set of m_isInitialized, thereby + removing the complex flag/heap logic that raced previously. +4. GlobalManager::InitializeGlobals() now conditionally calls the new, + thread-safe UsageDatabaseManager::Initialize(). + + +Security Impact +-------------------------------------------------------------------- +Without the lock an attacker could reliably corrupt camsvc global +structures and achieve code execution in the LocalSystem security +context, resulting in a full local privilege escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The serialisation with SRWLock eliminates concurrent entry and thus +removes the race window. The single atomic write to m_isInitialized is +performed while the exclusive lock is held, guaranteeing consistent +state visibility to all threads. No remaining unsynchronised access to +shared resources is observable in the patched code, so the fix is +considered effective for this vulnerability. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54109_mpssvc.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54109_mpssvc.dll.txt new file mode 100644 index 0000000..d110bdf --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54109_mpssvc.dll.txt @@ -0,0 +1,139 @@ +{'patch_store_uid': '55be8ad8-5a68-4e61-9274-e927d121c45f', 'file': 'mpssvc.dll', 'cve': 'CVE-2025-54109', 'date': 1757843450.17998, 'change_count': 45, 'kb': 'KB5065426', 'confidence': 0.38} +-------------------------------------------------------------------- +CVE-2025-54109 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – mpssvc.dll (Windows Defender Firewall service) +Functions affected: + • FWAddressAndIPAddressIntersect + • FwIntersectV4RangesAndLocalIPs + • FwIntersectV6RangesAndLocalIPs + • FwLoadBlobsRepos (collateral changes) + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / out-of-bounds memory access (CWE-843, side-effect is +potential out-of-bounds write/read) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The firewall service stores lists of IP address ranges in +_tag_FW_IPV4_RANGE_LIST and _tag_FW_IPV6_RANGE_LIST. Each list has a +DWORD element count at offset 0 followed by a variable-length array of +range records (8-byte pairs for IPv4, 32-byte pairs for IPv6). + +FwIntersectV{4,6}RangesAndLocalIPs() is supposed to calculate the +intersection of two range lists (a1 and a2) and write the resulting +ranges back into BOTH lists so that the caller may use either list. + +Pre-patch logic: + • The routine copied the intersected ranges into the buffer that + belongs to the SECOND argument (a2). + • It incremented a local output counter (v6 for IPv6, v5 for IPv4) + while writing. + • At function exit it only wrote this counter to *a1 (the first + list). The element count in *a2 was left unchanged. + +Resulting state after intersection: + a2->Count (still original) + a2->Ranges (now contains fewer entries than Count advertises) + +Any subsequent consumer that iterates a2 using its Count will walk past +valid memory and treat uninitialised memory as _tag_FW_*_RANGE +structures – a textbook type-confusion condition that can escalate into +out-of-bounds read or write, depending on the caller’s access pattern. +Because the objects are allocated inside privileged mpssvc and their +contents can be influenced indirectly by an unprivileged caller through +firewall RPC / WFP APIs, the flaw yields a local elevation of privilege. + +Patch behaviour: + • Introduces a second counter dedicated to the ranges written into + a2 (v6 / v6 for IPv6, v6 for IPv4). + • On return it updates BOTH length fields: + *a1 = number_of_elements_in_a1; + *a2 = number_of_elements_in_a2; // new + • Additional defensive rewrites protect loop indices and prevent the + possibility of writing beyond the destination buffer. + +Thus the two structures remain internally consistent and no over-run / +type confusion is possible. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// IPv6 – before (excerpt) +*(_OWORD *)(v10 + 32i64 * v6) = *v9; // write into a2 buffer +... +*(_DWORD *)v2 = v6; // only a1->Count updated +``` +```c +// IPv6 – after (excerpt) +*(_OWORD *)(v13 + i) = *v12; // write into a2 buffer +... +*(_DWORD *)v2 = v4; // a1 count +*(_DWORD *)a2 = v6; // a2 count – FIXED +``` +```c +// IPv4 – before (excerpt) +*(_DWORD *)(v4 + 8 * v5) = v9; // write into a2 buffer +... +*(_DWORD *)a1 = v5; // only a1->Count updated +``` +```c +// IPv4 – after (excerpt) +*(_DWORD *)(v4 + 8 * v6) = v10; // write into a2 buffer +... +*(_DWORD *)a1 = v5; // a1 count +*(_DWORD *)a2 = v6; // a2 count – FIXED +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged client supplies crafted firewall rules or address + objects (via RPC, WMI, NetFirewall API, or WFP BFE). +2. mpssvc loads the objects (FwLoadBlobsRepos) and eventually calls + FWAddressAndIPAddressIntersect. +3. FWAddressAndIPAddressIntersect dispatches to + FwIntersectV4RangesAndLocalIPs and/or FwIntersectV6RangesAndLocalIPs. +4. Pre-patch, intersection corrupts the length field of list #2. +5. Later code trusts a2->Count and overruns the backing buffer, + enabling memory corruption inside the service process. + +Attack Vector +-------------------------------------------------------------------- +Any local user who can create or modify firewall rules (e.g. through the +netsh advfirewall command or the Windows Filtering Platform API) can +supply maliciously-sized IPv4/IPv6 address ranges. When the service +processes the rule set, the inconsistent length field triggers the type +confusion and memory corruption in the high-privilege mpssvc context. + +Patch Description +-------------------------------------------------------------------- +• Introduced dedicated destination counters so writes into the second + range list are matched with a correct element count. +• Updated both list headers on exit. +• Added additional loop variables and flag logic to avoid stale state. +• Minor signature change in FWAddressAndIPAddressIntersect (now void) + reflecting that no status code is returned. +• Ancillary hardening and clean-up in FwLoadBlobsRepos to remove memory + leaks; unrelated to the core bug. + +Security Impact +-------------------------------------------------------------------- +Before the patch, inconsistent meta-data could lead to controlled +out-of-bounds access within the Windows Defender Firewall service, +allowing local attackers to corrupt heap structures and execute code +with service privileges (LOCAL SERVICE or, through further compromise, +SYSTEM). Microsoft assigned CVE-2025-54109. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code now keeps the size fields of both range lists in sync +with the actual number of elements written. No writes occur past the +allocated buffers, eliminating the immediate type-confusion vector. +Static inspection shows both IPv4 and IPv6 paths corrected; no legacy +callers rely on the previous return value of FWAddressAndIPAddressIntersect. +No residual pathway for mismatched size/count was observed, indicating +the fix is effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54110_ntoskrnl.exe.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54110_ntoskrnl.exe.txt new file mode 100644 index 0000000..d3a6dae --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54110_ntoskrnl.exe.txt @@ -0,0 +1,136 @@ +{'change_count': 712, 'date': 1757844066.365791, 'file': 'ntoskrnl.exe', 'kb': 'KB5065426', 'patch_store_uid': '8ceaf580-961a-430f-ad80-451621699df6', 'cve': 'CVE-2025-54110', 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-54110 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Kernel – Processor-Power-Management (Ppm) thread-priority +synchronisation logic (function PpmReleaseLock in ntoskrnl.exe). + + +Vulnerability Class +-------------------------------------------------------------------- +Integer underflow / wrap-around (CWE-190) leading to corrupted per- +thread priority-floor accounting and privilege escalation. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each thread owns an 8-bit reference counter array + KTHREAD::PriorityFloorCounts[32] +that tracks how many independent “priority floor” requests are active +for a given class of locks. Entry index 22 is reserved for Ppm locks. + +Prior to the patch PpmReleaseLock executed the following sequence: + 1. Acquire the current thread’s lock at IRQL DPC. + 2. Call KiClearPriorityFloor( CurrentThread ). + This **unconditionally zeroed** PriorityFloorCounts[22] and + cleared bit 22 (0x400000) from PriorityFloorSummary. + 3. Drop the thread lock and return. + +If the same thread owned *nested* Ppm locks, only the first call should +have decremented the counter. Because the old code bluntly forced the +counter to zero, every subsequent release operated on a value that was +already zero. The next internal decrement therefore wrapped the +unsigned byte to 0xFF, leaving the summary bit permanently set while +PriorityFloorCounts[22] appeared non-zero for the life-time of the +thread. + +Effect of wrap-around: +• Scheduler permanently believes that a priority floor request is still + active and keeps the thread at an artificially high priority. +• The thread now executes with Real-time / High priority irrespective + of normal quota or job restrictions, effectively granting an + unprivileged process scheduling dominance. Combined with known + techniques (e.g. CPU starvation of higher-integrity threads) this + enables a local Elevation-of-Privilege. + +Patch logic adds precise reference accounting: + • Reads the current counter (v5). If zero ⇒ bugcheck 0x157. + • Decrements the byte safely. + • Clears the summary bit only when the counter reaches zero. + • Re-evaluates the thread’s base priority via KiComputeThreadPriority + and lowers it if required. +Thus the integer underflow can no longer occur. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Before (simplified) +KiAcquireThreadLockRaiseToDpc(CurrentThread, &Local); +KiClearPriorityFloor((ULONG_PTR)CurrentThread); // zeroes counter ! +CurrentThread->ThreadLock = 0; +``` + +```c +// After +KiAcquireThreadLockRaiseToDpc(CurrentThread, &Local); +unsigned char cnt = CurrentThread->PriorityFloorCounts[22]; +if (!cnt) + KeBugCheckEx(0x157, CurrentThread, 0x16, 2, 0); +cnt--; +CurrentThread->PriorityFloorCounts[22] = cnt; +if (!cnt) { + CurrentThread->PriorityFloorSummary ^= 0x400000; + if (CurrentThread->Priority <= 31) { + unsigned int newP = KiComputeThreadPriority(CurrentThread, 0); + if ((int)newP < CurrentThread->Priority) + KiSetPriorityThread(CurrentThread, &Local, newP); + } +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Thread A acquires Ppm lock twice (nested) via kernel paths + – PpmAcquireLock increments PriorityFloorCounts[22] to 2. +2. First PpmReleaseLock call – counter cleared to 0 by vulnerable code. +3. Second PpmReleaseLock call – internal decrement of now-zero counter + wraps to 0xFF (integer underflow). +4. Summary bit never cleared; scheduler treats thread as permanently + floor-boosted. + + +Attack Vector +-------------------------------------------------------------------- +A local, already-running user-mode process triggers kernel code paths +that nest PpmAcquireLock / PpmReleaseLock (e.g. through repeated +NtPowerInformation calls or crafted ACPI/ power-policy IOCTLs). After +the wrap-around the attacker’s thread keeps elevated scheduling +priority, giving it the ability to starve or manipulate higher- +integrity code and gain system privileges. + + +Patch Description +-------------------------------------------------------------------- +• Added explicit reference count handling for PriorityFloorCounts[22]. +• Introduced bugcheck safeguard when the counter is unexpectedly zero. +• Clears PriorityFloorSummary bit only when the counter reaches zero. +• Recomputes and lowers the thread’s base priority when the floor is + removed. +These changes eliminate the unsigned-byte underflow and restore correct +scheduler state. + + +Security Impact +-------------------------------------------------------------------- +Before the fix an attacker could cause PriorityFloorCounts[22] to wrap +from 0 → 255, leaving the thread in a perpetual high-priority state. +This breaks scheduler isolation and can be used to achieve local +Elevation of Privilege. The bug operates entirely in kernel mode and +bypasses standard integrity-level checks; code-execution is not +required in kernel context, only the ability to invoke affected power +APIs. + + +Fix Effectiveness +-------------------------------------------------------------------- +The patch introduces: 1) counter-underflow detection, 2) safe +post-decrement logic, and 3) recalculation of thread priority. Because +any future attempt to release a non-owned lock now raises bugcheck +0x157 instead of wrapping, exploitation is effectively blocked. No +remaining paths in PpmReleaseLock bypass the new checks, so the fix is +considered effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54112_vhdmp.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54112_vhdmp.sys.txt new file mode 100644 index 0000000..1ce53d2 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54112_vhdmp.sys.txt @@ -0,0 +1,132 @@ +{'file': 'vhdmp.sys', 'cve': 'CVE-2025-54112', 'date': 1757843498.1917422, 'change_count': 33, 'patch_store_uid': 'b9f0560e-8f82-438f-81bf-95574d46a204', 'kb': 'KB5065426', 'confidence': 0.18} +-------------------------------------------------------------------- +CVE-2025-54112 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows kernel-mode driver vhdmp.sys (Virtual Hard Disk +(VHD) Mini-Port – Win32k storage stack) responsible for IO tracking +and surface management for virtual hard-disk files. + +Vulnerability Class +-------------------------------------------------------------------- +Race-condition–driven Use-After-Free (CWE-416). A shared rundown +reference counter protecting the per-disk IO-tracking structure was +manipulated non-atomically, allowing the structure to be freed while +still referenced. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +The per-disk structure VHD_IO_TRACKER is protected through a kernel +rundown reference (EX_RUNDOWN_REF RunRef = &VirtualDisk->IoTracker). +Two helper routines manage the lifetime of this tracker: + + • VhdmpiActivateIoTracking() – increments RunRef->Count and, on + the first reference, initialises per-CPU state. + + • VhdmpiDeactivateIoTracking() – decrements RunRef->Count and, once + the last reference is released, re-initialises the rundown object + so that ExWaitForRundownProtectionRelease() will succeed. + +Before the patch both functions updated the 32-bit field +RunRef[3].Count with plain C operators: + ++LODWORD(RunRef[3].Count); + LODWORD(RunRef[3].Count)--; + +Although the update is performed while holding a passive-level lock +(&RunRef[1]), other CPUs may still touch the same field when calling +ExAcquireRundownProtection(), because that fast-path is executed +without taking the passive lock. Consequently the following race is +possible: + +1. CPU-0 : enters DeactivateIoTracking() and executes + "RunRef[3].Count--", which brings the value to + zero and re-initialises the rundown object. +2. CPU-1 (almost + simultaneous) : executes ExAcquireRundownProtection(). Because + Count is now zero the rundown completes and + the memory occupied by VHD_IO_TRACKER (and the + underlying VHD_SURFACE) can be freed. +3. CPU-1 continues : still holds a pointer to the freed object and + later dereferences it, leading to a classic + use-after-free in kernel mode. + +An attacker able to trigger frequent Activate/Deactivate operations +(e.g. by opening/closing a diff-chain VHD from multiple threads) can +reliably hit the window and obtain arbitrary kernel-mode read/write +capabilities, escalating to SYSTEM. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – non-atomic manipulation +VhdmpiActivateIoTracking: + if (++LODWORD(RunRef[3].Count) == 1) { ... } + +VhdmpiDeactivateIoTracking: + if (LODWORD(RunRef[3].Count)-- == 1) + ExReInitializeRundownProtection(RunRef); + +// after patch – atomic manipulation + if (_InterlockedIncrement((volatile LONG*)&RunRef[3]) == 1) { ... } + ... + if (_InterlockedExchangeAdd((volatile LONG*)&RunRef[3], -1) == 1) + ExReInitializeRundownProtection(RunRef); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode code opens a VHD and issues IOCTLs that cause + VhdmpiActivateIoTracking() / VhdmpiDeactivateIoTracking() to be + called from multiple threads. +2. Two CPUs race on the non-atomic ++/-- instructions. +3. One CPU re-initialises the rundown object while the other still + believes it owns a reference. +4. VHD_IO_TRACKER and child VHD_SURFACE are freed by the rundown + completion path. +5. The stale pointer kept by the losing CPU is dereferenced inside a + subsequent I/O path (e.g. VhdmpiProcessEventInsert), corrupting + kernel memory. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker. No special privileges are required +beyond the ability to open a virtual hard-disk file (CreateFile on +.vhd / .vhdx), which is granted to standard users. A stress program +can hammer opens/closes or snapshot operations on multiple cores to +hit the race. + +Patch Description +-------------------------------------------------------------------- +1. Both ++ and -- on RunRef[3] are replaced with the corresponding + Interlocked* intrinsics, guaranteeing atomicity with respect to + ExAcquireRundownProtection(). +2. Additional synchronisation aids were added: + – VhdmpiDecoupleVirtualDiskSurface now waits on a per-surface + KEVENT to make sure no thread is inside the surface while it is + being destroyed. + – Several paths raise and lower reference counts with + InterlockedAdd/InterlockedIncrement. +3. Defensive waits (KeWaitForSingleObject) are introduced for builds + where Feature_H2E_WPA3SAE private flag is enabled. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch the reference count could drop to zero while still +referenced, resulting in freed memory being reused and later +accessed. An attacker could: +• Corrupt adjacent pool data structures, leading to arbitrary kernel + read/write. +• Execute code in kernel context and elevate privileges to SYSTEM. +The issue is therefore classified as an Elevation-of-Privilege in the +Windows kernel. + +Fix Effectiveness +-------------------------------------------------------------------- +The updated driver converts all reference-count manipulations to +atomic interlocked operations, closing the race window between the +rundown fast-path and Activate/Deactivate routines. Additional +synchronisation points (KEVENT waits) ensure surfaces cannot be +freed while still accessed. No remaining non-atomic updates to +RunRef->Count were located, so the fix is considered effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54114_cdp.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54114_cdp.dll.txt new file mode 100644 index 0000000..cff6b1f --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54114_cdp.dll.txt @@ -0,0 +1,119 @@ +{'kb': 'KB5065426', 'cve': 'CVE-2025-54114', 'patch_store_uid': '84bebcb6-0b80-441a-b89d-b2f711a3a440', 'date': 1757843898.708984, 'file': 'cdp.dll', 'change_count': 811, 'confidence': 0.27} +-------------------------------------------------------------------- +CVE-2025-54114 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows Connected Devices Platform Service (Cdpsvc), cdp.dll – +DeviceCollection::GetDeviceInfo + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition leading to CWE-822: Untrusted Pointer +Dereference / potential use-after-free, resulting in local Denial of +Service. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +DeviceCollection keeps a std::map (offset +40) that stores pointers +(std::_Ref_count_base*) to ICDPDeviceInfo objects. Prior to the patch +GetDeviceInfo(char const*) executed the following sequence: + +1. Construct a std::string key from the incoming device-id. +2. Search the internal map for that key. +3. Copy the stored shared_ptr (Ref_count_base) into the caller-supplied + out parameter. +4. Return without any synchronisation. + +Because the routine referenced both the map container and the refcount +object without holding the collection mutex (offset +8) two or more +threads could simultaneously: + • look up the same entry, + • increment / decrement the shared reference count, or + • erase the entry through another API path. + +A losing thread could end up copying a pointer that has already been +freed or whose reference count is in the process of being decremented +past zero. Subsequent dereference in user code or in Cdpsvc itself +causes an access violation, crashing the service and breaking all +applications that rely on Connected Devices Platform – a classic local +DoS. + +In short, the shared resource (map + Ref_count_base) was accessed +without mutual exclusion, allowing an authorized local attacker to race +service threads and provoke invalid pointer use. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (decompiled, simplified) +std::_Ref_count_base* GetDeviceInfo(const char* id) +{ + std::string key(id); + auto it = m_deviceMap.find(key); // no lock + if (it == m_deviceMap.end()) + return nullptr; + return it->second; // races with other threads +} +``` + +```c +// AFTER (key excerpts) +std::_Mutex_base::lock(&this->m_mtx); // acquire +std::string key(id); +_tree::find(...); +... +_Mtx_unlock(&this->m_mtx); // release +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker spawns several threads or processes. +2. Each thread issues the public API that ultimately reaches + DeviceCollection::GetDeviceInfo with the same device id. +3. One thread erases or releases the entry while another is copying it. +4. Freed pointer is dereferenced later in service context -> crash. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Any process able to open the Cdpsvc COM +interface and call the relevant method can race the service through +high-rate, parallel requests. No special privileges are required +beyond local execution. + + +Patch Description +-------------------------------------------------------------------- +The fix replaces the old, autogenerated stub with a hand-written +implementation that: +1. Introduces a member mutex (offset +8). +2. Calls std::_Mutex_base::lock / _Mtx_unlock around the map lookup and + shared_ptr copy. +3. Properly tidies temporary std::string objects. +4. Adds error handling: on failure it zeroes the out parameter and + decrements any transient refcounts. +These changes serialise access to the shared container and guarantee +that the reference count transitions are atomic with respect to lookup +and copy. + + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could crash Cdpsvc at will, causing +system-wide Connected Devices features (Bluetooth, wireless display, +etc.) to stop working until the service is restarted. Elevation of +privilege is not indicated; the impact is Denial of Service. + + +Fix Effectiveness +-------------------------------------------------------------------- +The added mutex fully protects the critical section in this function so +the specific race is resolved. Other Cdpsvc entry points were not +changed, therefore the overall robustness depends on similar protection +elsewhere (unknown). No bypass is evident for the addressed issue. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54115_vmwp.exe.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54115_vmwp.exe.txt new file mode 100644 index 0000000..e579399 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54115_vmwp.exe.txt @@ -0,0 +1,135 @@ +{'change_count': 30, 'kb': 'KB5065426', 'confidence': 0.11, 'date': 1757843691.4850814, 'cve': 'CVE-2025-54115', 'file': 'vmwp.exe', 'patch_store_uid': 'aa6cdbf6-89a6-44dd-b063-2bfd0d522b3b'} +-------------------------------------------------------------------- +CVE-2025-54115 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Hyper-V worker process (vmwp.exe) – class GmoMigration, function +DoMemoryTransferPass(). Code runs in the host context and is executed +when a live-migration source VM begins a memory-transfer pass. + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Concurrent execution using shared resource with improper +synchronisation (race condition) leading to memory corruption / pointer +confusion. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +GmoMigration keeps a large per-migration object whose members are +indexed by fixed offsets. Several of these members are concurrently +accessed by the main migration thread and by a thread-pool timer +callback that periodically reports progress. + +The vulnerable version mis-calculated the field indices when it +initialised the object at the start of every memory-transfer pass: + + * m_UpdateProgressTimer was at QWORD offset 47, but code + used offset 48. + * m_MigrationSource was at QWORD offset 49, but code + stored the pointer in offset 50. + * m_Statistics fields were zeroed in the wrong order, causing + a transient overlap with the two pointers above. + +As a consequence the following happened in real time: + +1. Main thread passes *(this+48) – i.e. a future statistics slot – to + SetThreadpoolTimer(). The thread-pool infrastructure believes this + value is a valid PTP_TIMER pointer and will later dereference it on + a worker thread. +2. A few instructions later the same memory location *(this+48) is + cleared to zero or overwritten with other data by the same function + or by another thread that legitimately updates statistics. +3. When the timer expires the worker thread dereferences the stale / + corrupted pointer, leading to use-after-free or arbitrary pointer + dereference inside the vmwp.exe process. + +Because the corruptible field is attacker-controlled through the live +migration API (e.g. size-related statistics or the +WorkerTaskMigrationSource pointer), a user with the ability to start a +migration can race the two threads and supply a crafted value that is +later treated as a kernel-mode function pointer, resulting in +escalation to the privilege level of the Hyper-V host worker process. + +No locks protect the shared fields; the bug is therefore a classic +race between the migration control path and the asynchronous timer +callback. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable initialisation sequence (simplified) +*((_QWORD*)this + 51) = 0; // stats +*((_QWORD*)this + 52) = 0; // stats +*((_QWORD*)this + 53) = 0; // stats +*((_QWORD*)this + 46) = 0; // stats +*((_QWORD*)this + 47) = 0; // stats +*((_QWORD*)this + 50) = a3; // SHOULD have been +49 +SetThreadpoolTimer(*((PTP_TIMER*)this + 48), &Due, 0x1F4, 0); +``` + +```c +// corrected sequence +*((_QWORD*)this + 52) = 0; +*((_QWORD*)this + 53) = 0; +*((_QWORD*)this + 51) = 0; +*((_QWORD*)this + 50) = 0; // zero statistics slot first +*((_QWORD*)this + 46) = 0; +*((_QWORD*)this + 49) = a3; // correct destination +SetThreadpoolTimer(*((PTP_TIMER*)this + 47), &Due, 0x1F4, 0); +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker (tenant admin) initiates VM live migration. +2. Hyper-V worker executes GmoMigration::DoMemoryTransferPass(). +3. Function calls SetThreadpoolTimer() with wrong field address. +4. Main thread or attacker-controlled data overwrites same location + before timer fires. +5. Timer callback executes on pool thread, dereferences corrupted + pointer and jumps to attacker-controlled address. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to manage VMs (or code running inside a +privileged guest) triggers multiple fast consecutive migration passes, +forcing the mis-initialisation and racing the timer callback to gain +control of the corrupted field. + + +Patch Description +-------------------------------------------------------------------- +The fix re-aligns all member-offset references: + +1. Correct timer pointer index: +48 -> +47. +2. Correct migration-source pointer index: +50 -> +49. +3. Zeroes the old slot (+50) before reuse to eliminate stale data. +4. Reorders zeroing of statistics fields so no live pointer is ever + overwritten after SetThreadpoolTimer is armed. + +No additional locking was required once the structure layout was used +consistently. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch a local authorised attacker could achieve elevation +of privilege on the Hyper-V host. By racing the progress-timer thread +against the migration control thread the attacker could cause +vmwp.exe to execute with a corrupted function pointer, providing +arbitrary code execution in the system context. + + +Fix Effectiveness +-------------------------------------------------------------------- +The fix removes the shared-memory overlap and therefore eliminates the +possibility of concurrently modifying the pointer used by the timer +callback. Without an overlapping field there is no longer a race +window, so the described privilege-escalation path is closed. +-------------------------------------------------------------------- \ No newline at end of file diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54894_lsasrv.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54894_lsasrv.dll.txt new file mode 100644 index 0000000..943b123 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54894_lsasrv.dll.txt @@ -0,0 +1,119 @@ +{'kb': 'KB5065426', 'change_count': 31, 'confidence': 0.21, 'date': 1757843558.1665506, 'file': 'lsasrv.dll', 'patch_store_uid': '088c2266-2443-4e1a-9bb7-fdeb31b20e02', 'cve': 'CVE-2025-54894'} +-------------------------------------------------------------------- +CVE-2025-54894 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – Local Security Authority Sub-System Service +(lsasrv.dll). The vulnerable logic resides in + • LsapWinRtCaptureClientAuthIdentity() + • NegpCaptureSuppliedCreds() + • SspiExAcquireCredentialsHandle() + +Vulnerability Class +-------------------------------------------------------------------- +Heap-based buffer overflow / out-of-bounds write (CWE-122). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NegpCaptureSuppliedCreds() receives a comma-separated list of security +package names from an unprivileged caller (RPC/LSA LPC). The routine +first counts the number of names (variable ‘v48’ in the original +listing) and allocates an array of 8-byte pointers with a size of + 8 * v48 bytes. + +Later, while it iterates over the list, the code may insert additional +entries that were **not** included in the initial count: + • names preceded by ‘!’ or ‘-’ cause a second pass that may add the + same package again; + • packages having the 0x40 (NEG_PACKAGE_SKIPEXT) flag cause the + function to enumerate the global NegPackageList and append further + packages that satisfy the requested negotiation flags. + +Because the allocation was based solely on the original token count, +writing these extra pointers overruns the heap buffer (`v99->Children`) +allocated for the pointer array. The write primitive is reachable in +user mode and is performed in the LSASS process under SYSTEM, leading to +memory corruption and potential elevation of privilege. + +Additional issues fixed in the same patch set: +– LsapWinRtCaptureClientAuthIdentity lacked several size checks when it + decrypted and re-marshalled supplied _SEC_WINNT_AUTH_IDENTITY_EX2 + blobs. Incorrect size tracking could propagate malformed lengths into + NegpCaptureSuppliedCreds, aggravating the overwrite window. +– SspiExAcquireCredentialsHandle relied on the unsafe helper routines + above and therefore inherited the overflow. + +Key conditions that trigger the overflow: +1. Caller supplies a credential buffer with ADT 512 (supplied creds). +2. `v18 & 1` is set so that the caller-supplied string is treated as + Unicode. +3. The comma-separated list contains enough specially-prefixed entries + to make the second enumeration loop add >NegPackageCount elements. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// original (simplified) +packageCount = v48; // number of parsed tokens +credArray = LsapAllocate(8 * packageCount); +... +for (each token) { + if (needExtraPackage) + credArray[cur++] = extraPkg; // <- no bounds check +} +``` + +```c +// patched +if (wil::details::FeatureImpl<...>::IsEnabled() && j >= NegPackageCount) +{ + status = SEC_E_BUFFER_TOO_SMALL; // -2146892963 + goto cleanup; +} +credArray[j++] = pkg; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Unprivileged client calls LsaLogonUser / AcquireCredentialsHandle and + provides a crafted buffer. +2. SspiExAcquireCredentialsHandle() ➜ NegpCaptureSuppliedCreds(). +3. NegpCaptureSuppliedCreds() parses the comma list, allocates an + undersized array, then appends additional packages (flags 0x40 / ‘!’ + / ‘-’). +4. Out-of-bounds write corrupts LSASS heap memory ➜ code execution under + SYSTEM. + +Attack Vector +-------------------------------------------------------------------- +Local attacker with the ability to call LSA APIs (e.g., via the SSPI / +LogonUser or S4U interfaces) sends a malicious credential buffer to +LSASS. No administrative rights are required. + +Patch Description +-------------------------------------------------------------------- +1. Introduced a running index variable (‘j’) that is compared against + the **actual capacity (NegPackageCount)** before every write to the + array. Exceeding the limit now returns SEC_E_BUFFER_TOO_SMALL. +2. Added additional size/length validation in + LsapWinRtCaptureClientAuthIdentity(): – verified offsets and lengths + of embedded strings, – converted several signed variables to + unsigned, – guarded allocations and frees with correct sizes. +3. Updated SspiExAcquireCredentialsHandle() to use the safer helper + routines and correctly propagate the new error codes. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could corrupt heap metadata or adjacent +objects inside the LSASS process, leading to elevation of privilege to +SYSTEM and possible escape of AppContainer / sandbox boundaries. + +Fix Effectiveness +-------------------------------------------------------------------- +The added capacity check prevents any write when the computed index +would exceed the allocated element count, fully neutralising the +overflow. Additional validation in helper routines removes secondary +paths that could still inject malformed length fields. No residual +unsafe writes were observed in the patched code. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54895_negoexts.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54895_negoexts.dll.txt new file mode 100644 index 0000000..5818053 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54895_negoexts.dll.txt @@ -0,0 +1,120 @@ +{'kb': 'KB5065426', 'file': 'negoexts.dll', 'change_count': 21, 'date': 1757853797.2639103, 'confidence': 0.19, 'patch_store_uid': '9b734c10-a0d9-4e2b-95c7-7526e2061403', 'cve': 'CVE-2025-54895'} +-------------------------------------------------------------------- +CVE-2025-54895 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft SSPI – SPNEGO Extended Negotiation (NEGOEX) Security +Support Provider, implemented in negoexts.dll. +All affected code lies in the class basessp::BaseSSP and helpers +exported from negoexts.dll that are reachable through standard SSPI +APIs used by LSA and network-authenticating services. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-190: Integer Overflow / Wraparound (leading to heap buffer +overflow and memory corruption). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. The core allocator wrapper is + void * BaseSSP::WSTAllocate(size_t cb) + implemented at vtable slot 30/31 of a BaseSSP instance. + +2. In the vulnerable build the routine casts the 64-bit parameter + ‘a2’ to 32 bits before calling the real heap allocator: + alloc_ptr((unsigned int)a2); + No upper bound is verified. + +3. If a caller supplies a value larger than 0xFFFFFFFF + (e.g. 0x1_0000_0008), the high 32 bits are silently truncated. + The heap manager therefore reserves only (cb & 0xFFFFFFFF) bytes, + while the caller believes the full 64-bit request succeeded. + +4. Immediately after the allocation the wrapper executes + memset(ptr, 0, a2); // uses full 64-bit length + which zeroes past the end of the buffer and corrupts adjacent + heap metadata / objects. + +5. Multiple higher-level helpers (WSTDuplicateStringEx and + WSTGetTargetHostName) rely on WSTAllocate for building UNICODE + strings that are later copied into process-supplied buffers + running in SYSTEM context. Therefore the overflow is reliably + reachable from user mode through standard SSPI InitiateSecurity + Context / AcceptSecurityContext calls that load negoexts.dll in a + privileged LSA process. + +6. Once heap memory is corrupted the attacker can hijack control + flow inside lsass.exe and run arbitrary code at LocalSystem + integrity, resulting in a local Elevation of Privilege. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// negoexts.dll – vulnerable version +void * __fastcall BaseSSP::WSTAllocate(BaseSSP *this, size_t cb) +{ + if (*((DWORD*)this + 52) == 1) + return ((ALLOC32)(*(QWORD*)this + 384))( (unsigned int)cb ); + void *p = ((ALLOC32)(*((QWORD*)this + 31)))( (unsigned int)cb ); + if (p) + memset(p, 0, cb); // cb is 64-bit, p size may be 32-bit + return p; +} + +// patched version +if (FeatureEnabled && cb > 0xFFFFFFFF) + return 0; // reject oversized request +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +SSPI client -> lsass.exe + InitiateSecurityContext()/AcceptSecurityContext() + NEGOEX package parses user-supplied token + WSTGetTargetHostName() or WSTDuplicateStringEx() + BaseSSP::WSTAllocate( userControlledLength ) <-- overflow + heap corruption in lsass.exe + +Attack Vector +-------------------------------------------------------------------- +Local attacker supplies a crafted NEGOEX authentication token (e.g. +via the standard SSPI API from a low-privilege process) that embeds a +very large length field (>4 GB). The token is processed inside the +privileged LSASS process, hitting the vulnerable allocation path and +corrupting the heap, which can then be exploited for code execution +as SYSTEM. + +Patch Description +-------------------------------------------------------------------- +1. A feature-flagged guard was inserted at the beginning of + BaseSSP::WSTAllocate: + if (cb > 0xFFFFFFFF) return 0; + The function now returns a 64-bit pointer cast (__int64) to avoid + accidental promotion/truncation. + +2. All in-tree callers (WSTDuplicateStringEx, WSTGetTargetHostName) + were refactored to call the fixed WSTAllocate instead of + re-implementing their own allocation logic. + +3. Legacy code paths that manually zeroed the buffer were removed – + the allocation helper now performs any necessary initialization. + +Security Impact +-------------------------------------------------------------------- +Before the fix any user who could trigger NEGOEX negotiation could +force lsass.exe to write beyond the end of a heap buffer, enabling +reliable elevation of privilege to SYSTEM. The attack requires no +special privileges other than the ability to start an authentication +sequence on the local machine. + +Fix Effectiveness +-------------------------------------------------------------------- +The added 64-bit length check eliminates the 32-bit truncation window; +requests larger than 0xFFFFFFFF are refused and no heap allocation is +performed, preventing the subsequent out-of-bounds memset/copy. All +known entry points were updated to use the same safe helper. No +remaining cast or unchecked arithmetic on allocation sizes was +observed in the patched code, indicating the vulnerability has been +fully neutralized. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54911_fvevol.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54911_fvevol.sys.txt new file mode 100644 index 0000000..5b9efbc --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54911_fvevol.sys.txt @@ -0,0 +1,97 @@ +{'change_count': 32, 'cve': 'CVE-2025-54911', 'file': 'fvevol.sys', 'date': 1757853919.075565, 'patch_store_uid': '090f9405-7989-4bcd-ac06-e37b6069c85a', 'kb': 'KB5065426', 'confidence': 0.29} +-------------------------------------------------------------------- +CVE-2025-54911 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows 10/11 kernel-mode driver fvevol.sys (BitLocker +volume filter) – routine AcquireRequestResources(). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-416: Use-After-Free (caused by corrupted heap pointer). + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +AcquireRequestResources() builds an internal request-context structure +(FVE_REQUEST_CTX in the text below, base held in rsi => v9). The +structure contains, among other fields: + offset 0x110 (272) PVOID DataBuffer + offset 0x110 (272) BYTE Flags (bit 4 == cached-pages) +In the pre-patch code those two fields occupied the same physical +bytes. The pointer is stored with + *((_DWORD*)v9+68) = 0; // zero DataBuffer (0x110) +while the flag is later manipulated via + v20 = v12 + 272; // char * to same location + *v20 |= 0x10; // set cached bit + *v20 &= ~0x10; // clear cached bit +Because the flag logic operates on a single byte, every modification +clobbers the low byte of the 64-bit DataBuffer pointer. When the +pointer is subsequently freed or dereferenced (e.g. in the error path +around LABEL_78 and the normal completion path in +FveReleaseRequest()), the driver uses a value that may already have +been: + • overwritten with 0x00/0x10, or + • previously released by ExFreePool/IoFreeMdl. +This produces a classic UAF window: the pool block can be reclaimed by +an attacker and later operated on by fvevol.sys with kernel privileges. + +Key parameters/structures affected + – FVE_REQUEST_CTX::DataBuffer (offset 0x110) + – FVE_REQUEST_CTX::Flags.cached (same address pre-patch) + – pool block allocated via ExAllocatePool2 / lookaside list. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// pre-patch – pointer and flag share the same byte(s) +*((_DWORD*)v9 + 68) = 0; // zero pointer (offset 0x110) +v20 = v12 + 272; // char * to same offset +*v20 |= 0x10u; // set flag -> clobbers pointer LSB +... +// post-patch – fields separated +*((_QWORD*)v9 + 34) = 0i64; // 8-byte pointer at 0x110 +v12[276] = 1; // flag moved to 0x114 (no overlap) +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User code issues IOCTL / IRP that causes fvevol!AcquireRequestResources. +2. Function allocates FVE_REQUEST_CTX (ExAllocateFromNPagedLookasideList). +3. IoGetFsTrackOffsetState succeeds -> cached-pages path chosen. +4. Flag bit 0x10 is set/cleared on *(ctx+0x110). +5. Later, error or completion path frees DataBuffer using the now + corrupted pointer, or continues to access it, producing UAF. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker sends a crafted sequence of BitLocker +IOCTLs that force the cached-pages branch, trigger an error path that +frees the buffer, and then re-uses the freed request structure, +allowing controlled memory to be operated on at kernel IRQL. + +Patch Description +-------------------------------------------------------------------- +1. Split the overlapped field: + • DataBuffer remains at 0x110 but expanded to 8-byte store + (`*((_QWORD*)v9 + 34)`) + • Flags.cached moved to 0x114 (`v12[276]`). +2. All flag operations changed to use the new offset – no more pointer + modification via bitmask. +3. Auxiliary tidy-ups: variable types widened, boolean tracking via + int, early initialisation changes – all related to the same layout + correction. + +Security Impact +-------------------------------------------------------------------- +By re-allocating the freed pool block, an attacker can gain arbitrary +kernel read/write, leading to local privilege escalation to SYSTEM +(confirmed by Microsoft: "Elevation of Privilege"). + +Fix Effectiveness +-------------------------------------------------------------------- +The patch eliminates field overlap; flag writes can no longer corrupt +DataBuffer, thereby preventing misuse of freed memory. No further code +paths write to the pointer location after free, so the UAF condition +is fully addressed. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54912_fvevol.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54912_fvevol.sys.txt new file mode 100644 index 0000000..69a5818 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54912_fvevol.sys.txt @@ -0,0 +1,120 @@ +{'kb': 'KB5065426', 'patch_store_uid': '090f9405-7989-4bcd-ac06-e37b6069c85a', 'date': 1757854007.7322445, 'confidence': 0.18, 'cve': 'CVE-2025-54912', 'change_count': 32, 'file': 'fvevol.sys'} +-------------------------------------------------------------------- +CVE-2025-54912 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows BitLocker driver (fvevol.sys). +All changed functions belong to the sub-request control path that +creates, schedules and completes encrypted read/write requests. + + +Vulnerability Class +-------------------------------------------------------------------- +Race-condition Use-after-free (CWE-416) caused by unsynchronised +bit-field updates inside a shared control structure. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Each encryption sub-request is described by a 0x120-byte +FVE_SUBREQUEST_CONTROL structure referenced by many worker and +completion routines (WriteEncrypt, ReadDecrypt, Ice paths, etc.). + +Offset 0x110-0x11F of this structure previously contained a single +8-bit flag field (Flags byte @+272). Different bits inside this byte +were reused for unrelated purposes: + bit0 (0x01) : completion marker + bit1 (0x02) : paging request + bit2 (0x04) : Ice request + bit3 (0x08) : status merged-once guard + bit4 (0x10) : redirected range present + ... +Most of those bits were modified by independent code paths while the +byte was updated with read-modify-write sequences that were only partly +protected by spin locks. Two examples: + • FveIoCryptoWorkSchedule set bit2 (Ice) under one lock. + • MergeStatusIntoControl set bit3 (merged) under a different lock and + cleared bit2&bit1 by re-writing the whole byte. + +Because the whole byte was OR/AND-ed, one thread could silently drop +bits written by another thread. If bit2 (Ice tag) was cleared after +the IRP extension had been allocated, later clean-up executed the ICE +free path while other threads were still using the extension. This +results in a classic use-after-free on the per-IRP ICE extension +object, allowing a local attacker with the ability to trigger many I/O +requests to execute code with kernel privileges. + +Corruption becomes observable inside + FvePerIoCryptoThrottleStart + WriteCompletionRoutine / ReadCompletionRoutine +when the freed work-item or spin-lock is accessed. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old // patched +BYTE Flags = *(BYTE*)(ctrl+272); BYTE isIce = *(BYTE*)(ctrl+274); +Flags |= 8; BYTE mergedSet = *(BYTE*)(ctrl+275); +*(BYTE*)(ctrl+272) = Flags; /* independent bytes, no bit-ops */ +``` +```c +if (!*(_BYTE *)(v10 + 272) & 4) // old test + ... + +if (*(BYTE*)(v10+274)) // new test + ... +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User space issues heavy, overlapped writes to a BitLocker volume. +2. FveTryInlineWriteEncrypt / WriteEncrypt builds several sub-requests. +3. FveIoCryptoWorkSchedule sets bit2 (Ice) in Flags. +4. Almost concurrently MergeStatusIntoControl sets bit3 (merged) and + unintentionally clears bit2 due to byte-wide update. +5. Worker path believes request is NOT Ice, frees ICE extension while + it is still referenced → dereference of freed object → EoP. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker triggers a high volume of overlapping +I/O on a BitLocker protected volume to create the racing conditions. +No admin rights are required; SYSTEM privileges are gained after the +use-after-free is exploited. + + +Patch Description +-------------------------------------------------------------------- +Microsoft split the overloaded Flags byte into three independent +bytes: + offset +273 : PagingRequest + offset +274 : IsIceRequest + offset +275 : StatusMergedOnce + +All code paths were updated to access those dedicated bytes instead of +bit-operations on the original Flags field. The byte at +272 is now +used only for immutable device properties. +Additionally, MergeStatusIntoControl was rewritten to avoid clearing +other state fields, and every previous "&4" / "|4" test was replaced +with a direct read of byte +274. + + +Security Impact +-------------------------------------------------------------------- +The race allowed kernel-memory use-after-free that can be converted +into arbitrary kernel code execution. Successful exploitation yields +local elevation of privilege (SYSTEM) and full machine compromise. + + +Fix Effectiveness +-------------------------------------------------------------------- +After the patch each logical flag is stored in its own byte and is no +longer subject to lossy read-modify-write sequences. Inter-thread +interference is therefore removed and the freed ICE extension can no +longer be double-freed or accessed after release, effectively fixing +CVE-2025-54912. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54913_windows.ui.xaml.maps.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54913_windows.ui.xaml.maps.dll.txt new file mode 100644 index 0000000..13d3547 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54913_windows.ui.xaml.maps.dll.txt @@ -0,0 +1,155 @@ +{'file': 'windows.ui.xaml.maps.dll', 'cve': 'CVE-2025-54913', 'kb': 'KB5065426', 'change_count': 6, 'patch_store_uid': '705159f2-96ae-4921-9077-d001134b9ae5', 'confidence': 0.18, 'date': 1757843376.186107} +-------------------------------------------------------------------- +CVE-2025-54913 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows.UI.Xaml.Maps – specifically the global MapControlSettings +infrastructure located in windows.ui.xaml.maps.dll (functions such as +MapControl::EnsureMapControlSettings, iterator helpers in the +XWinRT/Collections templates). + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race Condition +CWE-416: Use-after-free (secondary effect of the race) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Global singleton + Windows::UI::Xaml::Controls::Maps::s_pMapControlAdapterSettings is + lazily created the first time MapControl::EnsureMapControlSettings() + is called. + +2. OLD behaviour (before patch) + EnsureMapControlSettings() performed: + a. if (ptr == NULL) { + InternalRelease(ptr); // may free old instance + Create(...,&ptr); // allocate new settings + } + The whole block executes with **no synchronisation**. Two threads + entering simultaneously can interleave so that one thread frees the + settings object while the other still uses it, or two different + objects are created and one leaked. Because the pointer is shared + process-wide this produces a classic TOCTOU race leading to memory + corruption / UAF. + +3. Related template helpers (SimpleVectorIterator::MoveNext, GetMany, + XWinRT::detail::*::AtomicUpdate / InvalidationChecker) also relied on + ad-hoc compare-exchange loops that accessed shared version counters + and out-parameters without guaranteeing a consistent view when the + loop was retried. The same thread-safety issue could surface while + enumerating Map collections during the MapControl initialisation + window, amplifying the chance of corrupting heap structures freed by + the primary race. + +4. Privilege escalation path (design-time): A low-privileged caller + that can execute XAML code instantiates MapControl from different + threads (e.g. UI thread + background thread). By racing the + initialisation it can force use-after-free of an IMapControlSettings + COM object. Because COM interface vtables reside in controllable + memory after free, the attacker can redirect execution inside the + host process, which may run with a higher integrity level (for + example XAML Island inside a privileged UWP container), thus + achieving elevation of privilege. + +Affected data/fields: + • Windows::UI::Xaml::Controls::Maps::s_pMapControlAdapterSettings + • MapControl::s_mapControlSettingsInitOnce (added by fix) + • Shared version counters at offset +40 / +48 / +56 in the various + iterator structures. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// BEFORE (race window) +if (!s_pMapControlAdapterSettings) { + Microsoft::WRL::ComPtr<IUnknown>::InternalRelease( + (ComPtr<IUnknown>*)&s_pMapControlAdapterSettings); + CreateMapControlSettingsForDesigner(&s_pMapControlAdapterSettings, + v2); // unsynchronised +} +``` +```c +// AFTER (thread-safe) +if (InitOnceExecuteOnce(&MapControl::s_mapControlSettingsInitOnce, + MapControl::InitializeMapControlSettingsOnce, + 0, + &Context)) { + // Settings pointer created exactly once +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker creates two (or more) threads in the same process. +2. Each thread calls MapControl().EnsureMapControlSettings(). +3. Thread-interleave: + T1 checks ptr==NULL → true + T2 checks ptr==NULL → true + T1 releases (NULL) safely + T2 releases (still NULL) + T1 creates object A, stores pointer + T2 creates object B, overwrites pointer, then frees pointer to A + T1 continues to use pointer to A → UAF +4. Corrupted vtable ⇒ arbitrary code execution inside host process. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker code running inside a sandboxed / low-integrity app that +is able to load the XAML Maps control. The attacker spawns parallel +threads to hit the initialisation race and hijacks freed memory to gain +control of execution in the higher-privileged XAML host process. + + +Patch Description +-------------------------------------------------------------------- +1. Added static INIT_ONCE MapControl::s_mapControlSettingsInitOnce and + re-implemented EnsureMapControlSettings() to call + InitOnceExecuteOnce(), guaranteeing that the settings object is + created exactly once and that the pointer cannot be freed while other + threads are still using it. + +2. Introduced wil::Feature gating to allow early exit if the feature is + disabled, reducing surface area. + +3. Re-worked iterator helpers: + • New AtomicUpdates::AtomicUpdate() replaces hand-rolled CAS loops. + • Extra out-parameter passed to MoveNext() so error propagation no + longer races on stack-allocated memory. + • GetMany() now branches: if iterator already immutable it directly + calls AtomicUpdate; otherwise it still uses single-threaded path. + +4. Removed the unsynchronised InvalidationChecker path for these + collections. + + +Security Impact +-------------------------------------------------------------------- +Before the fix a local attacker could reliably trigger use-after-free or +heap corruption while MapControlSettings was constructed, leading to +arbitrary code execution inside a process that hosts XAML (e.g. a UWP +app running with UIAccess or a privileged service that renders XAML). +This yields a local Elevation of Privilege. + +After the fix the singleton is initialised once, atomically; iterator +state changes are performed through a safe helper, eliminating the data +race and removing the UAF primitive. + + +Fix Effectiveness +-------------------------------------------------------------------- +The use of InitOnceExecuteOnce() gives strong one-time semantics backed +by the OS; no further unsynchronised accesses to +s_pMapControlAdapterSettings remain. The template refactor moves all +shared-state mutation behind a single _InterlockedCompareExchange loop +that is free from the original out-parameter race. No residual race +windows are observable in the patched code; therefore the fix is +considered effective. + diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54915_mpssvc.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54915_mpssvc.dll.txt new file mode 100644 index 0000000..3ea45f9 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54915_mpssvc.dll.txt @@ -0,0 +1,152 @@ +{'confidence': 0.25, 'file': 'mpssvc.dll', 'cve': 'CVE-2025-54915', 'kb': 'KB5065426', 'date': 1757853991.4943757, 'patch_store_uid': '55be8ad8-5a68-4e61-9274-e927d121c45f', 'change_count': 45} +-------------------------------------------------------------------- +CVE-2025-54915 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows Defender Firewall Service (mpssvc.dll). Faulty +logic resides in the helper routines that compute the intersection of +user-supplied IP range lists with the local interface list: + + • FwIntersectV4RangesAndLocalIPs() + • FwIntersectV6RangesAndLocalIPs() + +These routines are called while the firewall engine (running as +NT\SYSTEM) validates and stores policy objects that can be provided +through documented, authenticated management APIs. + + +Vulnerability Class +-------------------------------------------------------------------- +Type confusion / memory corruption (CWE-843). An internal structure +is used as if it contained more elements than were actually written, +causing subsequent consumers to treat unrelated memory as struct +_fw_IPv[46]_RANGE objects. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Both IPv4 and IPv6 helper functions receive two in-place editable +structures: + + struct _tag_FW_IPVx_RANGE_LIST + { + DWORD Count; // number of valid range entries + <ranges>[Count] // FW_IPVx_RANGE (8 or 32 bytes) + }; + +Parameters: + a1 – caller-supplied range list (policy object) + a2 – list of local interface addresses + +The algorithm copies every overlapping range into *both* buffers while +walking them in lock-step. Because the write happens in the very same +memory area (no new allocation), two counters have to be updated so +that later code knows the real element count in each list. + +Bug before patch +---------------- +At the end of the loop only the first structure’s Count field was +updated: + *(_DWORD *)a1 = v5; // new count + // *** a2->Count left unchanged *** + +Moreover, the loop index that controlled writes into a2 (v6) was +incremented unconditionally, but the outer index (v4/i) –the value +finally stored back into a1– lagged behind when no overlap was found. +This produced two inconsistent views: + + • memory from a2 now contains up to v6 valid FW_IPVx_RANGE elements + but a2->Count still advertises the original (bigger) value; + • spare memory beyond the last valid element is uninitialised but + will later be consumed as FW_IPVx_RANGE records. + +Any later routine iterating over a2->Count therefore reads or writes +past the legitimate array and operates on attacker-controlled data of +an unexpected type. Because the structures are embedded in larger +policy blobs, this turns into a classic type confusion and enables +arbitrary memory corruption inside the firewall service process. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// IPv6 – before patch +... + for (...) { + ... + if (v8 == 1) + ++v6; // v6 is write-index for a2 + } + *(_DWORD *)v2 = v6; // a1->Count fixed + // a2->Count never updated – stale value +``` + +```c +// IPv6 – after patch + ... + *(_DWORD *)v2 = v4; // real count for a1 + *(_DWORD *)a2 = v6; // real count for a2 (new line) +``` + +Analogous corrections were made for the IPv4 variant. +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Management client (administrator or service) submits a firewall + rule that contains large, crafted IPv4/IPv6 address ranges. +2. Firewall engine calls FwIntersectVxRangesAndLocalIPs() while + validating the rule. +3. Intersection routine corrupts a2->Count, leaving it larger than the + real number of elements. +4. Subsequent code iterates over the stale count and treats out-of- + bounds memory as FW_IPVx_RANGE objects (type confusion). +5. Controlled corruption of heap data structures running in the + mpssvc.exe process leads to elevation of privilege. + + +Attack Vector +-------------------------------------------------------------------- +Local attacker with permission to create or modify firewall rules +(e.g., a service with SERVICE_CHANGE_CONFIG, or an admin in a locked- + down environment) submits a specially crafted rule containing address +ranges designed to mis-size the second list. No code execution +primitives are needed in kernel mode; exploitation happens entirely in +the user-mode service but results in SYSTEM privileges. + + +Patch Description +-------------------------------------------------------------------- +1. Added independent counters for the two range lists (v4/v6 vs + v6/v7) and updated both Count fields at function exit. +2. Re-organised inner loops so that the write-index is incremented only + when an actual overlap is recorded. +3. Similar fixes ported to IPv4 helper. +4. Ancillary hardening: assertions and feature-flagged clean-up in + FwFreeBlobRepos() and FwLoadBlobsRepos(), preventing stale pointers + and memory leaks uncovered during investigation. + + +Security Impact +-------------------------------------------------------------------- +Before the patch, a non-privileged but authenticated user could cause +mpssvc.exe to perform out-of-bounds reads/writes on the heap, leading +to memory corruption. Because the service runs as LocalSystem, a +successful attacker can execute arbitrary code with SYSTEM privileges +(local elevation of privilege). + + +Fix Effectiveness +-------------------------------------------------------------------- +The corrected code guarantees that: + • both list headers always contain the true element count; + • loop indices never exceed the respective Count values; + • callers can no longer be fooled into reading uninitialised memory. + +The additional assertions and feature-gated frees ensure that related +blob-repository structures remain consistent, further reducing exploit +surface. No residual path to the original mis-sized access remains +observable in the patched binary. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54916_ntfs.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54916_ntfs.sys.txt new file mode 100644 index 0000000..aa3226f --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54916_ntfs.sys.txt @@ -0,0 +1,118 @@ +{'file': 'ntfs.sys', 'cve': 'CVE-2025-54916', 'change_count': 149, 'date': 1757843784.2419298, 'confidence': 0.12, 'kb': 'KB5065426', 'patch_store_uid': '1dc2f3fb-6383-4814-a8d5-7d77b86b8669'} +-------------------------------------------------------------------- +CVE-2025-54916 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +ntfs.sys – helper routine MoveAttributeToOwnRecord() + + +Vulnerability Class +-------------------------------------------------------------------- +Stack-based buffer overflow (CWE-121) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +MoveAttributeToOwnRecord() moves an NTFS attribute list entry from one +MFT record to a newly cloned record. While doing so the function must +copy the attribute’s name string. The name length (bytes to copy) is +stored in variable “v11” and may be larger than 0x10 bytes. + +1. The code declares a 16-byte stack buffer `v87` and initialises a + pointer `PoolWithTag` to this buffer: + PoolWithTag = v87; + +2. When the name is longer than 0x10 the code attempts to obtain + dynamic memory: + if (v11 > 0x10u) + PoolWithTag = ExAllocatePoolWithTag(...); + +3. The return value of ExAllocatePoolWithTag() is **not checked**. If + the allocation fails the pointer remains NULL. Immediately after + the branch the following copy is performed, using the unvalidated + pointer and the untrusted length: + memmove(PoolWithTag, &Src[NameOffset], v11); + +4. Two failure scenarios therefore exist: + • Allocation succeeds but the length is still >0x10 – copy writes + beyond the fixed 16-byte fallback buffer. + • Allocation fails – copy is performed to address 0x0 leading to + an access violation. + +Because the overflow is on the stack and the attacker fully controls +both the copied data and its length (via the on-disk $ATTRIBUTE_LIST +entry), arbitrary code can be executed in kernel context when the file +system processes the crafted record. + +Affected structures / fields + – ATTRIBUTE_LIST_ENTRY.NameLength + – local stack buffer `v87[16]` in MoveAttributeToOwnRecord() + – pointer `PoolWithTag` + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – MoveAttributeToOwnRecord +PoolWithTag = v87; // 16-byte stack buffer +Srca[1] = v87; +if (v11 > 0x10u) { + PoolWithTag = ExAllocatePoolWithTag(PagedPool, v11, 'NtfA'); + Srca[1] = PoolWithTag; // may be NULL on failure +} +memmove(PoolWithTag, // unchecked destination + &Src[NameOffset], // attacker-controlled source + v11); // attacker-controlled length +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User supplies a crafted disk image / remote SMB share containing an + MFT record whose $ATTRIBUTE_LIST entry has: + – NameLength > 0x10 + – Data that overflows target buffer +2. NTFS mounts / accesses the record. +3. NtfsMoveFileRecord triggers MoveAttributeToOwnRecord(). +4. ExAllocatePoolWithTag() returns NULL (or overflow length > 0x10). +5. memmove() overruns 16-byte stack buffer → corruption / RCE. + + +Attack Vector +-------------------------------------------------------------------- +Local or remote attacker places a malicious NTFS image (e.g. VHD, +USB-stick, or network share) that is mounted by Windows. No additional +privileges are required; code runs in kernel context once the crafted +record is parsed. + + +Patch Description +-------------------------------------------------------------------- +The new version still allocates a temporary buffer but rewrites the +whole routine and propagates allocation failure back to the caller. +Key fixes: + 1. Result of ExAllocatePoolWithTag() is validated. When the call + fails the function aborts with STATUS_INSUFFICIENT_RESOURCES + instead of copying. + 2. Copy length is now bounded by the size of the destination buffer – + dynamic if allocation succeeded, 16 bytes otherwise. + 3. Many auxiliary pointer/size calculations were converted from + 16-bit to 32-bit to prevent inadvertent truncation. + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could reliably achieve arbitrary kernel +memory corruption and thus execute arbitrary code (RCE) or trigger a +system crash (DoS). Successful exploitation yields kernel-level +privileges and compromises the entire operating system. + + +Fix Effectiveness +-------------------------------------------------------------------- +Code paths that copy the attribute name now bail out when the allocation +fails and never copy more than the buffer can accommodate. No further +unchecked memmove() using attacker-controlled length remains. The +stack overflow condition is therefore effectively removed. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54917_urlmon.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54917_urlmon.dll.txt new file mode 100644 index 0000000..5ccee71 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54917_urlmon.dll.txt @@ -0,0 +1,119 @@ +{'confidence': 0.34, 'change_count': 30, 'file': 'urlmon.dll', 'kb': 'KB5065426', 'patch_store_uid': '3f6a756a-e722-40c3-8cca-f409af73c699', 'cve': 'CVE-2025-54917', 'date': 1757843528.3363457} +-------------------------------------------------------------------- +CVE-2025-54917 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +urlmon.dll – URL-to-Zone resolution and special-directory mapping +(CMapPathToSpecialDirHelper, CSecurityManager::MapUrlToZonePrivate, +GetZoneFromFilePath, ExtractZoneFromFile, etc.) + +Vulnerability Class +-------------------------------------------------------------------- +Security-feature bypass / protection-mechanism failure +(CWE-693) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Before the patch the routine +CMapPathToSpecialDirHelper::GetLocalDirType() copied the supplied +path (parameter a2) into the caller-provided buffer pszPath with a +home-grown loop: + – The caller passed the maximum length in *a6 (often 260). + – The loop blindly wrote two bytes at a time until it either reached + the original *a6 counter or hit a NUL terminator. + – If the incoming name was longer than the buffer, the loop truncated + the string and then forced a NUL. The function did **not** return + an error unless the counter reached zero exactly, so silent + truncation was treated as success. + +The truncated string was subsequently used to + 1. remove the file part (PathRemoveFileSpecW), + 2. compare the directory against the helper’s cache, and + 3. decide which special directory type (Local, Profile, None) the + path belongs to. +Because the comparison was carried out on the truncated value, an +attacker could craft a long path whose prefix matched a trusted local +folder once it had been cut off. MapUrlToZonePrivate and +GetZoneFromFilePath therefore assigned Local Machine/Intranet zone to +content that originated from an untrusted location. + +Additional weak points fixed by the patch reinforced the bypass: + • PathPrefixMatchesWithSpecialDir() used the same unsafe copy logic. + • MapUrlToZonePrivate accepted device paths ("\\.\" / "\\;" ) and + incorrectly normalised them, again allowing remote resources to be + treated as local. + • ExtractZoneFromFile relied on an out-of-range index when the ADS + (Zone.Identifier) lookup failed, defaulting to Zone 1. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +v11 = *a6; +... do { + v15 = *(_WORD *)&v13[v14]; + if(!v15) break; + *(_WORD*)v13 = v15; // unchecked copy + v13 += 2; --v11; +} while (v11); +*(_WORD*)v16 = 0; // forced NUL, no error returned +``` + +```c +// after +v10 = StringCchCopyW(pszPath, *a6, a2); +if (FAILED(v10)) goto error; // copy is now bounded & error-checked +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker supplies a crafted file:/// or UNC path to IE / WebBrowser + host. +2. CSecurityManager::MapUrlToZonePrivate → + CMapPathToSpecialDirHelper::PathPrefixMatchesWithSpecialDir → + GetLocalDirType. +3. GetLocalDirType truncates the long path, mis-classifies it as a + trusted directory and returns DirectoryType = Local. +4. MapUrlToZonePrivate therefore returns URLZONE_LOCAL_MACHINE (or + INTRANET). +5. The caller grants elevated privileges to the untrusted content. + +Attack Vector +-------------------------------------------------------------------- +Any network attacker able to entice a victim to open a specially +crafted long path/URL (for example from a remote share or malicious +website) can cause urlmon to map the resource to the Local or Intranet +zone, bypassing the intended zone-based security restrictions. + +Patch Description +-------------------------------------------------------------------- +• Replaced all manual copy loops with StringCchCopyW (bounded copy that + propagates failure). +• Introduced FindCacheEntry() helper and centralised cache lookup. +• Added PathIsPrefixWrapperW and IsDriveAndNotIPv6 to normalise and + validate paths, and gated legacy behaviour behind feature flags. +• MapUrlToZonePrivate now rejects device paths unless explicitly + allowed and uses new wrappers when converting FILE:// URLs. +• ExtractZoneFromFile was rewritten to use GetZoneFromAlternateData + Stream, validated array indexes, and removed silent fall-backs. +• Numerous feature-flag checks were tightened; default is now “fail + closed”. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could get remote content executed in a +more privileged security zone, defeating zone-isolation, mark-of-the- +web and ActiveX download restrictions. This enables further attacks +such as running ActiveX controls without prompt or relaxing cross-zone +navigation rules. + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code eliminates silent truncation, enforces buffer limits +and adds explicit error handling. Directory comparisons are now +performed on the full, validated path, and device / reparse-point +paths are normalised or rejected. These changes close the discovered +bypass path and materially harden zone mapping; no residual unsafe +copies were observed in the modified routines. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54918_msv1_0.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54918_msv1_0.dll.txt new file mode 100644 index 0000000..856bb8f --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54918_msv1_0.dll.txt @@ -0,0 +1,161 @@ +{'cve': 'CVE-2025-54918', 'change_count': 49, 'file': 'msv1_0.dll', 'patch_store_uid': '3c97029c-c2b8-4d7a-8ac7-639ad928acdb', 'confidence': 0.21, 'date': 1757853977.2752466, 'kb': 'KB5065426'} +-------------------------------------------------------------------- +CVE-2025-54918 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NTLM authentication package (msv1_0.dll – NTLM SSP). The +following routines are directly involved: + • SsprMICHandshakeMessagesNew (replaces legacy CngRsa32Compat_MD5* + helpers) + • NlpDecryptCacheEntry + • SspSignSealHelper + • SsprMakeSessionKey + • LsaApLogonUserEx2 +All belong to the authentication logic executed in LSASS on the +server-side as well as inside the client SSP. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Authentication / weak cryptography (CWE-287, CWE-327). The +original implementation relied on single-round MD5 and RC4 to build +or verify NTLM MICs, session keys and cached-credential HMACs. Those +algorithms are no longer considered collision- or tamper-resistant and +can be forged by a network attacker that already possesses a valid +account. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. MIC / handshake hash + Old code: + CngRsa32Compat_MD5Init/Update/Final were fed by several NTLM + messages and finally CngRsa32Compat_MD5Final returned the 16-byte + MD5. Nothing prevented an attacker from creating second + pre-images – a well-known property of MD5 – so the MIC could be + forged. + + Patch: + New helper SsprMICHandshakeMessagesNew creates a *fresh* BCRYPT + hash handle (ALG 0x91) every time and re-computes the digest over + three explicit buffers that the caller passes (challenge, + negotiate flags, channel-binding). The handle is destroyed right + afterwards, removing any attacker-controlled state. + +2. Cached-credential decryption (NlpDecryptCacheEntry) + Old path used RC4 and MD5-HMAC via the CngRsa32Compat stubs. + Because RC4 is a stream cipher without integrity protection an + offline attacker that reads NL$KM could tamper with the encrypted + blob and then recompute the MD5 to keep the check value intact – + LSASS would happily accept the modified cache entry. + + Patch introduces: + • Optional feature switch 609789243 that replaces RC4 with AES-CBC + (BCryptEncrypt/GenerateSymmetricKey). + • Hash integrity moved from custom HMAC-MD5 to BCrypt SHA-256 + (same ALG 0x91) together with explicit length checks and zero + clearing of secrets. + +3. Message signing / sealing (SspSignSealHelper) + The helper originally relied on RC4-KDF + CRC32 for integrity. A + forged CRC is trivial. The new version, when the same feature flag + is enabled, derives a keyed SHA-256 hash and uses AES or duplicated + CNG keys; many fast-fail branches were replaced by proper NTSTATUS + propagation. + +4. Session-key creation (SsprMakeSessionKey) + Legacy code directly inserted the 16-byte random challenge into the + context; when NTLM2-session was negotiated the key was only RC4 + protected. The patch generates an explicit symmetric AES key and + encrypts the session key before it leaves kernel space. + +5. Main logon routine (LsaApLogonUserEx2) + Large patch but two relevant changes: + • new call sites switched to the hardened helpers above; no MD5 + helper remains. + • additional audit / telemetry but no functional change. + +In short, every place where MD5/RC4 was still used to build or verify +an NTLM protection checksum is now replaced by SHA-256/AES through the +BCrypt API. Because the old primitives are breakable, an attacker +could manufacture a handshake or cached entry that passes all +verifications and is accepted as authentic. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// old – MD5 on caller-supplied state +CngRsa32Compat_MD5Final(phHash); // returns 16-byte digest + +// new – re-compute inside function, caller cannot inject state +BCryptCreateHash(0x91, &hHash, 0,0,a1,0x10,0); +BCryptHashData(hHash, a3, a2, 0); +BCryptHashData(hHash, pbInput, a4,0); +BCryptHashData(hHash, a7, cbInput,0); +BCryptFinishHash(hHash, pbOutput, 0x10,0); +``` +```c +// old – RC4 + HMAC-MD5 integrity (truncated) +CngRsa32Compat_rc4_key(&hKey); +CngRsa32Compat_rc4(&hKey, len, buf); +CngRsa32Compat_HMACMD5Final(&phHash, tag); + +// new – AES + SHA-256 (behind feature flag) +BCryptGenerateSymmetricKey(0x71,&phKey,0,0,secret,0x10,0); +BCryptEncrypt(phKey, buf, len, NULL, NULL,0, buf,len,&cbOut,0); +BCryptCreateHash(0x91,&phHash,0,0,secret,0x10,0); +BCryptFinishHash(phHash, tag, 0x10,0); +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Victim host accepts NTLM authentication. +2. Attacker crafts MD5-collision (or RC4 stream flip) so that + a) MIC handshake in SsprMICHandshakeMessages validates, or + b) Cached NL$ entries hash correctly, or + c) CRC32 in SspSignSealHelper matches. +3. LSASS calls the old helper – returns success. +4. Logon is accepted and a token for the supplied LogonId is created. +5. Attacker now operates with elevated privileges (typically + authenticated as the spoofed user or SYSTEM in a relay). + +Attack Vector +-------------------------------------------------------------------- +• Remote: Any protocol that supports NTLM (SMB, RPC, HTTP, etc.). +• Local: Tampering with %SYSTEMROOT%\System32\config\SECURITY cache to + inject a forged NL$ entry. +In all scenarios the attacker must already possess *some* valid NTLM +credentials but can elevate or impersonate a different account by +bypassing integrity checks. + +Patch Description +-------------------------------------------------------------------- +1. Removed every call to legacy CngRsa32Compat_* MD5 / RC4 helpers. +2. Introduced SsprMICHandshakeMessagesNew that recomputes the MIC with + a fresh BCrypt hash per call. +3. Re-implemented NlpDecryptCacheEntry, SspSignSealHelper and + SsprMakeSessionKey to use BCrypt SHA-256 and AES (handles 0x91 / + 0x71) while preserving a fallback behind Feature_609789243. +4. Added extensive NTSTATUS propagation, WPP traces and zero-clearing + of key material. +5. LsaApLogonUserEx2 updated to call the hardened helpers and to clear + secrets after use. + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could produce MD5 collisions or exploit +RC4 bit-flipping to spoof the MIC, cached credentials, or NTLM +sign/seal tags, allowing privilege escalation across the network or +from cached logon data. After replacing the weak primitives with +modern, collision-resistant ones, forging those values is no longer +feasible, blocking the Elevation-of-Privilege vector. + +Fix Effectiveness +-------------------------------------------------------------------- +The old helpers are gone; every affected path now contains: + • A fresh BCrypt* call chain (hash or AES) – state cannot be injected. + • Strict status checking – any failure bubbles up. + • Secret buffers are wiped and handles destroyed. +Because the vulnerable MD5/RC4 code is completely bypassed the issue +is effectively remediated. No regression paths that still reach the +legacy helpers were found in the patched binary. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54918_ntlmshared.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54918_ntlmshared.dll.txt new file mode 100644 index 0000000..66f2947 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54918_ntlmshared.dll.txt @@ -0,0 +1,108 @@ +{'kb': 'KB5065426', 'cve': 'CVE-2025-54918', 'change_count': 3, 'confidence': 0.29, 'patch_store_uid': 'a1c68f1b-3751-404d-bcd9-96badc64ba43', 'date': 1757853816.848807, 'file': 'ntlmshared.dll'} +-------------------------------------------------------------------- +CVE-2025-54918 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows NTLM authentication logic inside ntlmshared.dll +(MsvpLm20GetNtlm3ChallengeResponse, MsvpAvlGet and related helpers). + +Vulnerability Class +-------------------------------------------------------------------- +Improper Authentication – CWE-287 + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +NTLMv2 messages carry a variable-length Target-Info list consisting of +‘AV pairs’. Each pair begins with a 16-bit AV-ID, followed by a length +field and the value bytes. Callers search this list with the helper +MsvpAvlGet( AvList , AvId , Size ). + +Before the patch MsvpAvlGet completely ignored the requested AvId +(parameter a2). The routine was hard-coded to succeed only when the +current pair’s ID equalled constant 7: + if ( (_WORD)i == 7 ) return v3; +As a consequence every caller that looked for a *different* AV-ID +always failed. In particular +MsvpLm20GetNtlm3ChallengeResponse() tries to fetch the AV_PAIR_TIMESTAMP +(ID 7) *or* other IDs (e.g. Channel-Bindings) depending on the call +site. When the wanted ID was not 7 the lookup returned NULL, the code +assumed “timestamp not present” and simply replaced it with the local +system time by calling NtQuerySystemTime(). + +Because the server timestamp is part of the NTLMv2 response hash, +feeding the client’s current time instead of the received time removes +the freshness guarantee. An attacker able to capture a legitimate +NTLMv2 response can replay it at any later moment (within the NTLM 7- +day window) and be authenticated, effectively bypassing anti-replay +logic and elevating privilege across the network. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// ntlmshared.dll – before patch +int *__fastcall MsvpAvlGet(int *a1, __int64 a2, unsigned int a3) +{ + ... + for ( i = *a1; (int)a3 > 0 && a3 >= HIWORD(i)+4; i = *v3 ) + { + if ( (_WORD)i == 7 ) // <- hard-coded AV-ID + return v3; + if ( !(_WORD)i ) // end of list + return 0; + a3 += -4 - HIWORD(i); + ... + } +} +``` +```c +// after patch +if ( (unsigned __int16)i == a2 ) // compare with requested AvId + return v3; +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Server receives NTLM_AUTHENTICATE message. +2. LSA calls MsvpPasswordValidate -> MsvpLm20GetNtlm3ChallengeResponse. +3. Routine calls MsvpAvlGet to extract AV_PAIR_TIMESTAMP. +4. Hard-coded lookup fails → timestamp deemed missing. +5. NtQuerySystemTime() fills a fresh value; NTLMv2 hash is recomputed + with attacker-controlled data. +6. Server accepts replayed / forged NTLMv2 response → privilege + escalation. + +Attack Vector +-------------------------------------------------------------------- +A network attacker who can capture a valid NTLMv2 AUTHENTICATE message +(e.g. via MITM, SMB relay, or capture on another connection) resends +that message to a target service. Because the timestamp is ignored and +re-generated locally, the server accepts the stale response and grants +the attacker the captured user’s privileges. + +Patch Description +-------------------------------------------------------------------- +1. MsvpAvlGet now compares the current pair’s AV-ID with the *caller + supplied* AvId parameter instead of the fixed constant 7. +2. The function signature is updated (a2 changed to int), and callers + are adapted. +3. Additional refactoring/feature-flag code ( + MsvpNtlm3*ResponseNew, ValidateResponseNew, etc.) is unrelated to the + core fix. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix any NTLMv2 session could be replayed within the 7-day +window, allowing an authenticated but unprivileged attacker to reuse +captured credentials and authenticate as the victim to arbitrary +services. This results in an Elevation of Privilege across the +network. + +Fix Effectiveness +-------------------------------------------------------------------- +The comparison is now parameterised; the faulty constant is removed. +All call sites pass the correct AV-ID, so timestamps and other +important AV pairs are now reliably located and verified. No new +memory or logic issues are visible in the patch, so the mitigation is +considered effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54919_win32k.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54919_win32k.sys.txt new file mode 100644 index 0000000..e4607e1 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-54919_win32k.sys.txt @@ -0,0 +1,146 @@ +{'patch_store_uid': '39e8eb88-5045-4fd8-bb20-53978eb2c087', 'change_count': 10, 'file': 'win32k.sys', 'kb': 'KB5065426', 'cve': 'CVE-2025-54919', 'date': 1757853829.0405438, 'confidence': 0.19} +-------------------------------------------------------------------- +CVE-2025-54919 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows win32k.sys – Graphics / Session-management call-out +layer (W32SessionAttachAndCalloutDispatch, W32CalloutDispatchThunk and +helpers). + +Vulnerability Class +-------------------------------------------------------------------- +Race condition (CWE-362) that leads to use-after-free / stale function +pointer dereference and ultimately arbitrary kernel-mode code +execution. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +At the centre of win32k’s "call-out" infrastructure is the routine +W32SessionAttachAndCalloutDispatch(). For request code 44 it looked up +an arbitrary session, entered that session’s process, and executed a +function pointer that lives inside the per-session WIN32KSESSIONSTATE +table: + + state = W32GetSessionStateForSession(SessionId); + proc = state->KernelCalloutTable->Dispatch; + (*proc)(Arg1,Arg2,Arg3); + +Prior to the patch the code performed this sequence without holding any +synchronisation primitive that guarantees the lifetime of either the +session state object or the target process : + +1. No push-lock was held while the process reference returned by + W32GetReferencedSessionProcessWithTag() was being used. +2. The function pointer was fetched and invoked outside any lock – the + table could be freed or re-initialised concurrently. +3. The helper W32GetReferencedSessionProcessWithTag() itself only + returned a _KPROCESS pointer; it did **not** return/retain the + corresponding session state, so the caller had no way to keep that + memory alive. + +Because gLock / gSessionProcessLifetimeLock were dropped between the +lookup and the dereference, a racing thread that terminates the session +(or re-initialises gSessionGlobalSlots after a logoff) can make the +pointer refer to freed pool memory. A local attacker can then reclaim +this memory with controlled data and have win32k dereference it in +kernel context, achieving arbitrary-code execution. + +The same missing-lock pattern existed in several helpers (e.g. +W32AttachToSessionAndExecute__lambda_3f4683…, NtUserBuildHwndList, +NtUserBuildHimcList, ApiSetpResolveHost) which all copied the same +unsafe session-state dereference logic. + +Structures / fields affected: + • gSessionGlobalSlots[] / gLowSessionGlobalSlots (pointer array) + • _tagWIN32KSESSIONSTATE.KernelCalloutTable + 0x10 / 0x30 / … + • gLock / gSessionProcessLifetimeLock (missing or dropped too early) + • Argument structure at a3 for request 44 (mode 1 / 2 selector) + +The bug is purely a time-of-check vs time-of-use gap – no bounds or type +checks were missing; the state simply went away while the pointer was +still in use. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch – W32SessionAttachAndCalloutDispatch (mode 1 path) +v7 = GetCurrentProcessSessionId(); +v8 = *(PFN)(*(_QWORD *)(*(_QWORD *)( + W32GetSessionStateForSession(v7)+0x88) + 0x18) + 0x10); +if (v8) + return v8(a1, a2, a3); // <--- no lock, session may be gone +``` +```c +// before patch – W32GetReferencedSessionProcessWithTag +ExAcquirePushLockSharedEx(&gLock,0); +state = W32GetUserGdiSessionStateForSession(id); +// 'state' is read, but the function returns only the KPROCESS pointer – +// caller loses the reference as soon as the lock is released. +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker thread calls a syscall that reaches + W32CalloutDispatchThunk(). +2. Thunk issues request code 44 -> W32SessionAttachAndCalloutDispatch(). +3. Function fetches session state & call-out pointer without lock. +4. Second attacker thread triggers session logoff / destruction so that + the session state is freed. +5. Original thread executes the now-dangling function pointer -> EoP / + kernel RCE. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user code. Any process that can issue win32k +syscalls (e.g. via GDI/User APIs) can create the racing threads and +control the freed memory. + +Patch Description +-------------------------------------------------------------------- +1. W32SessionAttachAndCalloutDispatch fully rewritten: + • Introduces tight critical-region windows and shared push-locks + (gLock) while walking gSessionGlobalSlots. + • Keeps the lock until after the target function pointer has been + *called* or a reference is explicitly held. + • Adds rigorous NULL / session-existence tests and returns + STATUS_PROCEDURE_NOT_FOUND when the pointer is absent. + • Iterates safely over all sessions when mode == 2. + +2. W32GetReferencedSessionProcessWithTag gains a third parameter that + returns a referenced pointer to the session state, ensuring the + memory is retained as long as the caller needs it. + +3. All helper lambdas (…_2da4d1…, …_6eea85…) now call the revised helper + and keep the process attached only while the session lock is held. + +4. High-level thunks (NtUserBuildHwndList, NtUserBuildHimcList, + W32CalloutDispatchThunk, etc.) were updated to fetch the per-session + function pointers only after validating that the corresponding slot + exists. + +5. Several code paths add feature-gating checks + (RtlQueryFeatureConfiguration) and explicit STATUS_NOT_IMPLEMENTED + fall-backs. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix an attacker could reliably cause win32k to call a +function pointer that points into attacker-controlled pool memory, +leading to arbitrary execution in the kernel (privilege escalation or +remote code execution via a graphical session such as RDP). The issue +is rated as "Remote Code Execution" because the vulnerable code is +reachable through graphics APIs exposed over Remote Desktop / Remote +App sessions. + +Fix Effectiveness +-------------------------------------------------------------------- +The new implementation keeps either a push-lock or an explicit object +reference for the entire lifetime of the session data being used, and +verifies the presence of every function pointer before invocation. +This removes the TOCTOU window and makes it infeasible to race the +pointer. No remaining unprotected dereferences were observed in the +modified code paths, suggesting the fix is comprehensive, though other +untouched call-sites must still obey the new contract of +W32GetReferencedSessionProcessWithTag(). diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55223_dxgkrnl.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55223_dxgkrnl.sys.txt new file mode 100644 index 0000000..09b5b7b --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55223_dxgkrnl.sys.txt @@ -0,0 +1,139 @@ +{'patch_store_uid': 'd684b50b-6751-470f-a207-df1d66ac8df9', 'change_count': 233, 'kb': 'KB5065426', 'confidence': 0.25, 'date': 1757843864.1211956, 'file': 'dxgkrnl.sys', 'cve': 'CVE-2025-55223'} +-------------------------------------------------------------------- +CVE-2025-55223 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows dxgkrnl.sys (DirectX Graphics Kernel), specifically +CompositionSurfaceObject list-management and token processing logic. + + +Vulnerability Class +-------------------------------------------------------------------- +Primary: CWE-362 Race Condition +Secondary: CWE-416 Use-After-Free (triggered by the race) + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +CompositionSurfaceObject::NotifyTokenInFrame originally performed its +own synchronization and list maintenance: + +1. It took an exclusive pushlock located at this+0x30 (field offset + 0x48 in the disassembly) with ExAcquirePushLockExclusiveEx. +2. It walked a doubly-linked list anchored at this+0x78 (offset 0x120) + looking for a child surface (node *i) whose stored tokenId matched + the caller-supplied CToken ( *((QWORD*)i-1) == *((QWORD*)a2+7) ). +3. On a match it executed the child’s virtual method at vtable+0x80 + (*(i-3)+0x128) => ChildSurface::NotifyTokenInFrame(). +4. IF the virtual call succeeded AND returned *a3 == TRUE, the parent + removed every **subsequent** node in the list: + v9 = i->Flink; + while (v9 != Sentinel) { unlink(v9); dec(Count); Release(v9); } + +The virtual call is fully under control of the child object. Nothing +prevents that code from: + • Re-entering the parent object; + • Dropping the parent’s pushlock (it is public to children); + • Destroying or unlinking list nodes, including the same nodes the + caller will access immediately afterwards. + +When control returns, NotifyTokenInFrame continues to trust that + + v9, v11 = v9->Flink and *((CompositionSurfaceObject**)v11 + 1) + +still reference valid memory. If the child thread has already freed or +re-used any of those nodes, the parent dereferences freed memory, +leading to a write-what-where or execution of a stale vtable pointer. +Because the code runs inside the graphics kernel, an attacker with +normal user-mode access to D3DKMT interfaces can exploit the race to +execute arbitrary code with kernel privileges. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before patch (excerpt) +for (i = this->ListHead.Flink; i != &this->ListHead; i = i->Flink) { + if (*((UINT64*)i - 1) == *((UINT64*)token + 7)) { + status = i->Vtbl->NotifyTokenInFrame(i, token, &found); + if (NT_SUCCESS(status) && found) { + v9 = i->Flink; // start after match + while (v9 != &this->ListHead) { + v11 = v9->Flink; // NEXT + unlink(v9); // manipulates list + this->Count--; // --entries + v9->Vtbl->Release(v9, 1); // may free object + v9 = v11; // continue on stale ptrs + } + } + break; + } +} +``` + +```c +// after patch (excerpt) +status = CPushLock::AcquireLockExclusive(this + 0x48); +if (NT_SUCCESS(status)) { + status = CCompositionSurface::NotifyTokenInFrame(this + 0x40, + token, + found, + flags); + CPushLock::ReleaseLock(this + 0x48); +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. User-mode code creates several surfaces that share a parent + CompositionSurfaceObject and obtains handles to their CToken values. +2. Two or more threads concurrently call the KMT ioctl that reaches + CompositionSurfaceObject::NotifyTokenInFrame. +3. Thread A matches a node, enters the virtual method, and the child + releases or destroys later nodes in the list, dropping the lock. +4. Thread A returns; the parent continues to iterate over already freed + nodes, causing a UAF. +5. Corrupted pointers are reused, giving the attacker arbitrary kernel + read/write or EIP control. + + +Attack Vector +-------------------------------------------------------------------- +Local, from an ordinary user account. Requires access to D3DKMT / +DirectComposition APIs which are available in all standard sessions. +No admin or special privileges are needed to trigger the race. + + +Patch Description +-------------------------------------------------------------------- +The patched build removes all ad-hoc list traversal and manual unlinking +from CompositionSurfaceObject::NotifyTokenInFrame. The function now + +1. Uses CPushLock::AcquireLockExclusive/ReleaseLock wrappers which + guarantee balanced acquisition and release and return an NTSTATUS. +2. Delegates token processing to CCompositionSurface::NotifyTokenInFrame + (this+0x40), passing through the caller’s parameters. +3. Does *not* touch the child list after the virtual call returns; all + cleanup is handled inside the lower-level routine that owns the list. + +By removing post-callback list manipulation the window for concurrent +modification and stale-pointer dereference is closed. + + +Security Impact +-------------------------------------------------------------------- +A successful exploit yields kernel-mode execution in the graphics stack, +allowing full local elevation of privilege (ring-0 arbitrary code). +Secondary impact includes potential kernel crashes (DoS). + + +Fix Effectiveness +-------------------------------------------------------------------- +The dangerous code paths that dereferenced list nodes after an +unbounded callback are gone, and locking is now abstracted. No stale +pointer is accessed after the virtual call, eliminating the UAF. No +additional race entry points are visible in the patched diff, so the +fix is considered effective barring undisclosed paths. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55224_win32kbase_rs.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55224_win32kbase_rs.sys.txt new file mode 100644 index 0000000..a0802fe --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55224_win32kbase_rs.sys.txt @@ -0,0 +1,141 @@ +{'confidence': 0.3, 'change_count': 6, 'cve': 'CVE-2025-55224', 'kb': 'KB5065426', 'file': 'win32kbase_rs.sys', 'patch_store_uid': 'bf8a92b5-8b0d-4ffa-9845-95aacec6031e', 'date': 1757843961.2844994} +-------------------------------------------------------------------- +CVE-2025-55224 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Win32k (win32kbase_rs.sys) – Region (GDI region / rgncore) handling +routines: +• rgncore::RegionCore::set_to_scans_from() +• rgncore::RegionCore::subtract() +• rgncore::fill_path::<impl RegionCore>::fill_path_with_outline_generic() +• rgncore::RegionCore::combine() + + +Vulnerability Class +-------------------------------------------------------------------- +• CWE-362 – Race Condition (concurrent access to shared RegionCore + buffers) +• CWE-416 – Use-After-Free / stale-pointer dereference after concurrent + resize failure + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +RegionCore objects keep an array of 32-bit scan descriptors allocated by +RawVecInner<T>::try_reserve(). When memory growth fails the allocator +returns the sentinel value 0x8000000000000001 to signal +STATUS_NO_MEMORY, **not** a usable pointer. + +1. Old code (combine / subtract / fill_path) called + RawVecInner::try_reserve() or finish_grow(). +2. If the allocator returned the sentinel, the functions continued to + treat the ‘new buffer’ fields as valid. Typical pattern: + if (try_reserve(...) != 0x8000000000000001) { ok } else { + if (Feature_IsEnabled()) + set_to_scans_from_no_resize(dst, src); // keeps pointer + else + set_to_scans_from(dst, src); // may *free* & re- + allocate the buffer + } +3. While the current thread still holds **references taken before the + failed grow**, set_to_scans_from() could free the old array and place + it in the look-aside list. Another CPU may subsequently reuse the + pool item, so the stored RegionCore pointer becomes dangling. +4. Later loops ( e.g. subtract’s wall-copy loop or + fill_path_with_outline_generic’s poly-line construction ) iterate + through the stale pointer, reading or writing freed memory. Because + the memory can be re-assigned concurrently, crafted data can be + pivoted into controlled code-execution gadgets. + +Trigger parameters / structures +• RegionCore header: [size, capacity, ptr] at +0x08 .. +0x18 +• Scan tables referenced from +0x08 +• RawVecInner<Scan> sentinel: 0x8000000000000001 + +A small Region that deliberately exhausts the look-aside list forces +finish_grow() to fail, schedules a second thread that reuses the scan +pool entry, and re-enters the vulnerable loop – winning the race and +causing a write-primitive inside freed memory. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// combine (before) +if (try_reserve(dst, old_len, need, 4,4) == 0x8000000000000001ULL) { + if (Feature_IsEnabled()) + set_to_scans_from_no_resize(dst, src); + else if (set_to_scans_from(dst, src)) // may realloc & free + return 1; // stale ptr left alive +} +``` + +```c +// set_to_scans_from (before) +alloc::finish_grow(..,&v36); +if (v36 == 1) { + v15 = 0x8000000000000001; // *error* kept in v15 + goto LABEL_29; // later used as size/pointer +} +``` + +```c +// subtract (before) +if (alloc::finish_grow(..) == 1) { + // growth failed, but code continues using v37 as new buffer +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +User → NtGdiCombineRgn / NtGdiFillPath / NtGdiExtCreateRegion → +win32k!RGNOBJ::bCombine → RegionCore::combine() → +RawVecInner::try_reserve() → failure sentinel propagated → +set_to_scans_from(_no_resize) frees or reuses the same scan array → +loop in subtract/fill_path uses stale pointer → memory corruption / RCE. + + +Attack Vector +-------------------------------------------------------------------- +Local, from a low-integrity or sandboxed process that can issue win32k +GDI region API calls. No special privileges are required; a malicious +PDF/EMF rendered via GDI in a remote browsing context can exercise the +race. + + +Patch Description +-------------------------------------------------------------------- +1. In set_to_scans_from() the grow-failure path now returns immediately + (error 14) instead of continuing with the sentinel. Additional upper + bound checks ( v8 > 0x3FFFFFFFFFFFFFFF ) were added. +2. combine() reworked: on allocation failure it *always* calls + set_to_scans_from_no_resize(); the branch that could reallocate while + stale references exist was removed. +3. subtract() / fill_path_with_outline_generic() now abort on every + alloc-failure; they no longer use the possibly invalid buffer. Large + blocks of defensive bounds-checks were inserted. +4. All touched functions validate index vs. capacity before every access + (numerous panic_bounds_check insertions). + + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could: +• Obtain an arbitrary use-after-free of region-scan arrays shared across + threads. +• Race the look-aside allocator to re-fill the freed pool block with + controlled data, leading to out-of-bounds reads/writes. +• Escalate to kernel-mode RCE from a sandboxed process (confirmed by + Microsoft: CVE-2025-55224, CVSS 8.8). + + +Fix Effectiveness +-------------------------------------------------------------------- +The patched code propagates allocation errors instead of ignoring the +sentinel, removes the re-allocation race path, and enforces strict +bounds checks before every scan access. No stale pointer is left after +failure, eliminating the UAF window. The fix is effective assuming all +call-sites adopt the new early-return logic. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55224_win32kfull.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55224_win32kfull.sys.txt new file mode 100644 index 0000000..0182a32 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55224_win32kfull.sys.txt @@ -0,0 +1,124 @@ +{'file': 'win32kfull.sys', 'cve': 'CVE-2025-55224', 'change_count': 633, 'date': 1757843993.998181, 'kb': 'KB5065426', 'confidence': 0.13, 'patch_store_uid': 'b3952593-9f8c-45cf-b9b4-2e0e409c0bb2'} +-------------------------------------------------------------------- +Unknown-CVE Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kfull.sys – several window-manager system-call handlers (e.g. +SfnINOUTLPPOINT5, SfnINOUTLPRECT, SfnINOUTLPWINDOWPOS, SfnINOUT +LPSCROLLINFO, SfnINLPDELETEITEMSTRUCT, NtUserfnTOUCHHITTESTING, etc.) + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362 Race Condition (TOCTOU on user buffers) +CWE-416 Use-After-Free / invalid pointer dereference (derived) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch a family of win32k system-call helpers copied +structures supplied from user mode by performing a single ProbeForRead +(or ProbeForWrite) and then dereferencing the user pointer *after the +probe*, very often outside the thread’s win32k critical section. The +sequence was generally: + + 1. ProbeForRead/Write(struct, sizeof …) + 2. Leave crit / release locks + 3. *dereference the same user pointer* to build internal objects or + to return data to user mode + +Because the memory is still owned by user mode, a racing thread in the +same process can re-map or free the page(s) between steps (1) and (3). +When step (3) executes, kernel code operates on attacker-controlled or +invalid memory, allowing information disclosure or arbitrary pointer +reads / writes that ultimately lead to privilege escalation or remote +code execution in the kernel. + +Examples from the diff (old code): + v12 = *(_OWORD **)(v17 + 16); + if ( v12 + 2 < v12 || (unsigned __int64)(v12 + 2) > MmUserProbeAddress ) + v12 = (_OWORD *)MmUserProbeAddress; // range check only + v13 = v12[1]; // dereference user page + +The patch introduces a new helper RtlCopyFromUser() that explicitly +copies the user buffer into a kernel stack buffer *before* the thread +releases the critical section. All vulnerable call sites were +rewritten to: + + • allocate fixed stack storage + • call RtlCopyFromUser(dst, user_ptr, size) + • operate only on the kernel copy + +Affected structures/parameters include: + − tagPOINT / tagRECT + − tagWINDOWPOS + − SCROLLINFO + − DELETEITEMSTRUCT + − TOUCH_HIT_TESTING_INPUT + − arbitrary 0x64-byte buffer in NtUserSetDesktopColorTransform + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before (SfnINOUTLPPOINT5) +v20 = (_QWORD *)(v28 + 16); +if ( v20 + 5 < v20 || (ULONG_PTR)(v20 + 5) > MmUserProbeAddress ) + v20 = (_QWORD *)MmUserProbeAddress; +*(_OWORD*)a4 = *(_OWORD*)v20; // user memory still live +``` + +```c +// after +__int128 tmp = 0; +RtlCopyFromUser(&tmp, (void*)user_ptr, 0x20); +*(__int128*)a4 = tmp; // safe – kernel copy +``` +``` + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker calls one of the affected win32k system calls supplying a + pointer P inside a user-writable page. +2. Kernel probes P, returns to user mode (e.g. via KeUserModeCallback) + or leaves the win32k crit-section. +3. Attacker replaces, frees or remaps the page backing P. +4. Kernel later dereferences P believing it is still trustworthy, + leading to a controlled read/write or use-after-free → code exec. + +Attack Vector +-------------------------------------------------------------------- +Local, low-privileged code in a GUI session. No special permissions are +needed beyond owning a window / thread able to invoke the affected +syscalls. + +Patch Description +-------------------------------------------------------------------- +1. Added a new wrapper RtlCopyFromUser() that combines ProbeForRead with + an immediate memcpy into kernel storage. +2. Replaced all direct user-pointer dereferences with copies into + fixed-size stack buffers. +3. Where nested pointers were returned from user mode (e.g. via + KeUserModeCallback) the code now copies those secondary buffers with + RtlCopyFromUser before use. +4. Several helper routines (ProbeForRead_0, PWInsertAfter, etc.) were + rewritten to use safe helpers (HMValidateHandleNoSecure, etc.). + +Security Impact +-------------------------------------------------------------------- +Prior to the patch an attacker could win a race to point the kernel at +controlled memory, allowing: + • Arbitrary kernel memory disclosure or modification + • Use-after-free on kernel objects + • Ultimately kernel-mode code execution and full OS compromise + +Because many GUI syscalls are reachable from a sandbox, exploitation +can be chained for privilege-escalation or, in RDP / Hyper-V graphics +channels, remote code execution in the host. + +Fix Effectiveness +-------------------------------------------------------------------- +All affected paths now copy the entire user supplied structure under the +protection of the probe, before any locks are dropped. No remaining +kernel dereferences of user memory were observed in the patched code. +The race window is thus closed and the vulnerability is fully +mitigated. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55226_dxgkrnl.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55226_dxgkrnl.sys.txt new file mode 100644 index 0000000..859b623 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55226_dxgkrnl.sys.txt @@ -0,0 +1,136 @@ +{'confidence': 0.38, 'cve': 'CVE-2025-55226', 'file': 'dxgkrnl.sys', 'patch_store_uid': 'd684b50b-6751-470f-a207-df1d66ac8df9', 'kb': 'KB5065426', 'change_count': 233, 'date': 1757854128.569031} +-------------------------------------------------------------------- +CVE-2025-55226 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows graphics kernel driver (dxgkrnl.sys). Vulnerable +routine: CTDR_GDI_RESET_THREAD::IsTdrAdapterUsedInSessionCallback(). + + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Improper synchronization / race condition that can lead to a +use-after-free of a session-owned adapter pointer. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Prior to the patch the callback determines whether the calling session +is using the TDR (Timeout Detection & Recovery) adapter by walking +internal data structures without holding any lock or reference: + +1. SessionData = DXGSESSIONMGR::GetSessionDataForSpecifiedSession(a3) + returns a pointer to a DXGSESSIONDATA object. No reference count is + taken and no lock is held after the call returns. + +2. The code immediately dereferences + *(SessionData + 0x486C) // offset 0x486C == 18508 + to obtain a DXGSESSIONDATA::pPairedRenderAdapter pointer, stores it + in a register and then compares it with the adapter pointer kept in + CTDR_GDI_RESET_THREAD::m_pAdapter // *(this + 0x20) + +3. A concurrent thread that tears down the remote session or switches + the paired adapter can free or repurpose the same DXGSESSIONDATA + object in the time window between step 1 and 2. The callback then + reads a dangling pointer. + +4. The read value is further split into low/high DWORDs and compared. + If the freed memory is re-allocated by an attacker-controlled + buffer, the comparison operates on attacker data. + +Consequence: a TOCTOU race can turn into a kernel use-after-free read, +potentially escalating to code execution when combined with typical +heap-spray techniques inside the kernel pool used by session manager +objects. + +The exposure exists only for non-console sessions (a3 != ActiveConsole) +where IsTdrAdapterUsedInSessionCallback() is executed in the context of +an RDS/CSC session. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable fragment (before patch) +SessionDataForSpecifiedSession = DXGSESSIONMGR::GetSessionDataForSpecifiedSession(...); +if (!SessionDataForSpecifiedSession) + return 0; + +v7 = *(this + 0x20); // m_pAdapter +SessionDataForSpecifiedSession = + *(DXGSESSIONDATA **)((char *)SessionDataForSpecifiedSession + 0x486C); + +if (!v7) { + if (!HIDWORD(SessionDataForSpecifiedSession)) + return (DWORD)SessionDataForSpecifiedSession != 0; + return 1; +} +return SessionDataForSpecifiedSession == *(DXGSESSIONDATA **)(v7 + 0x19C); +``` + +```c +// safe fragment (after patch) +DXGSESSIONMGR::GetPairedRenderAdapterInRemoteSession(..., &LuidLow); +... +v8 = *(m_pAdapter + 0x19C); +if (LuidLow == LOWDWORD(v8)) { + if (LuidHigh == HIGHDWORD(v8)) + return true; +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker runs in a remote desktop (or similar) session. +2. Attacker starts intensive create/destroy of remote sessions or fast + adapter switching to force free/reuse of DXGSESSIONDATA. +3. Concurrently a TDR reset is initiated, invoking the vulnerable + IsTdrAdapterUsedInSessionCallback() for that session. +4. Callback dereferences freed object, reading attacker-supplied data. +5. Corrupted data path can lead to arbitrary kernel memory access when + later used, enabling code execution in kernel context. + + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated user. Requires ability to log on to a non-console +session (RDP, RemoteFX, etc.) and perform rapid session tear-down or +adapter switching to win the race. + + +Patch Description +-------------------------------------------------------------------- +1. Converts return type from char to bool for clarity. +2. Short-circuits the function for console sessions early. +3. Introduces a new, synchronised helper + GetPairedRenderAdapterInRemoteSession() + which safely returns the adapter LUID as two DWORDs while holding + the session lock, eliminating the need to dereference + DXGSESSIONDATA::pPairedRenderAdapter directly. +4. Performs all comparisons on the returned LUID values, avoiding any + access to potentially freed memory. +5. Removes the convoluted pointer/byte casts, preventing accidental use + of high DWORDs of stale pointers as boolean flags. + + +Security Impact +-------------------------------------------------------------------- +Before the fix, a local attacker could win a race and cause the kernel +callback to read from freed memory, potentially leading to corruption of +internal adapter state and ultimately remote code execution within the +Windows kernel (SYSTEM privileges). Successful exploitation breaks the +kernel driver sandbox and may crash the system or allow privilege +escalation. + + +Fix Effectiveness +-------------------------------------------------------------------- +The updated code path that was vulnerable now relies exclusively on a +thread-safe helper which returns simple POD values; no object +pointers are dereferenced post-lookup. This removes the UAF window and +aligns the comparisons with proper locking discipline. No remaining +unsynchronised dereferences were observed for the remote-session path, +so the patch is considered effective against the reported issue. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55228_win32k.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55228_win32k.sys.txt new file mode 100644 index 0000000..a071c92 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55228_win32k.sys.txt @@ -0,0 +1,130 @@ +{'confidence': 0.11, 'cve': 'CVE-2025-55228', 'date': 1757843691.058951, 'change_count': 10, 'kb': 'KB5065426', 'patch_store_uid': '39e8eb88-5045-4fd8-bb20-53978eb2c087', 'file': 'win32k.sys'} +-------------------------------------------------------------------- +CVE-2025-55228 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel graphics subsystem (win32k.sys) – session-management and +call-out dispatch code paths that attach to an arbitrary session / +process (W32* family of helpers). + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race condition in combination with CWE-416: use-after-free. + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several helper paths that execute graphics / USER call-outs inside an +arbitrary logon session rely on + W32GetReferencedSessionProcessWithTag(sessionId , Tag) + to obtain a referenced KPROCESS pointer for the target session. In +the pre-patch implementation the function: +1. Took gLock in SHARED mode, +2. Retrieved a USER/GDI per-session structure by executing + UserGdi = W32GetUserGdiSessionStateForSession(sessionId); +3. Released gLock and, still holding only the separate + gSessionProcessLifetimeLock, copied the process pointer located at + UserGdi+0x28 (= offset 40) and conditionally referenced it with + ObfReferenceObjectWithTag. + +The function then returned that pointer to the caller +(W32SessionAttachAndCalloutDispatch and several *AttachToSession* code +paths). The returned pointer is subsequently passed to + PsAcquireProcessExitSynchronization() + KeStackAttachProcess() + KeUnstackDetachProcess() + +Unfortunately, the USER/GDI session object that contains the process +pointer can be freed asynchronously as soon as the owning logon session +terminates. The global gLock used in step (1) does not protect the +memory lifetime of the UserGdi structure once it has been returned – it +only guards the slot array. Therefore a window exists between the +moment the address is taken and the moment the caller dereferences the +embedded KPROCESS pointer. A racing thread that logs off / deletes the +session can free the same memory, so the caller works on freed memory +leading to a classic kernel use-after-free. When the freed region is +re-allocated by attacker-controlled data, that data is treated as a +valid KPROCESS structure and is passed into +KeStackAttachProcess, giving the attacker arbitrary kernel-mode code +execution. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// win32k!W32GetReferencedSessionProcessWithTag (before patch) +KeEnterCriticalRegion(); +ExAcquirePushLockSharedEx(&gLock,0); +UserGdi = W32GetUserGdiSessionStateForSession(sessionId); +... +ExReleasePushLockSharedEx(&gLock,0); +KeLeaveCriticalRegion(); // lifetime of UserGdi no longer guarded + +ExAcquirePushLockSharedEx(&gSessionProcessLifetimeLock,0); +proc = *(PVOID *)(UserGdi + 0x28); // pointer taken from possibly + // freed UserGdi structure +if (proc) + ObfReferenceObjectWithTag(proc, Tag); +ExReleasePushLockSharedEx(&gSessionProcessLifetimeLock,0); +``` +No additional reference is kept on the UserGdi structure itself, so the +window between the two locks enables the race. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker thread A: rapidly logs off a session (frees UserGdi + structure). +2. Attacker thread B: + NtGdi/Dxg/USER system call + -> W32CalloutDispatchThunk + -> W32SessionAttachAndCalloutDispatch + -> W32AttachToSessionAndExecute… + -> W32GetReferencedSessionProcessWithTag (returns dangling + KPROCESS pointer) + -> KeStackAttachProcess( fake_pointer ) => kernel RIP control. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated attacker running code inside a user session can +start two racing threads: one that continuously terminates and creates +sessions, and another that repeatedly issues the affected graphics / +USER system call. No special privileges are required beyond the +ability to create sessions (logon), making the issue viable for local +EoP or RCE in services that talk to win32k from a sandbox. + +Patch Description +-------------------------------------------------------------------- +1. Re-implemented W32GetReferencedSessionProcessWithTag: + • Accepts an optional out-parameter to return the immutable + SessionState pointer. + • Uses W32GetSessionStateForSession (not the volatile UserGdi + object). + • Keeps both gLock *and* gSessionProcessLifetimeLock until after a + reference to the KPROCESS object is safely taken, guaranteeing the + backing memory cannot disappear. +2. Callers were rewritten to consume the new interface and to perform + extra validation (session-id sanity checks, bugchecks on -1, NULL + checks, additional error paths returning STATUS_INVALID_SESSION). +3. Many call-out thunks now bail out with STATUS_GRAPHICS_INVALID if + the session state or function pointer is missing instead of blindly + dereferencing. + +Security Impact +-------------------------------------------------------------------- +A successful race gives the attacker an arbitrary, attacker-supplied +pointer that win32k treats as a valid KPROCESS. Subsequent +KeStackAttachProcess/KeUnstackDetachProcess operations execute in the +context of that fake process, allowing full kernel-mode arbitrary code +execution. This yields local privilege escalation to NT AUTHORITY\SYSTEM +and, under specific circumstances (e.g. when the graphics call originates +in a remotely reachable service), remote code execution. + +Fix Effectiveness +-------------------------------------------------------------------- +The patch closes the lifetime gap by holding both relevant locks while +the KPROCESS is looked up and referenced, and by returning the stable +SessionState pointer to the caller so no subsequent unprotected access +is required. Additional NULL/invalid-session checks convert what used +to be silent dereferences into controlled STATUS_* +failures. No further exploitable path is observable in the updated +code; the fix appears effective against the previously described race. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55228_win32kbase.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55228_win32kbase.sys.txt new file mode 100644 index 0000000..8353bc2 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55228_win32kbase.sys.txt @@ -0,0 +1,139 @@ +{'patch_store_uid': '6f91c739-5c54-4375-9321-fa8ca7db437b', 'cve': 'CVE-2025-55228', 'file': 'win32kbase.sys', 'date': 1757843688.700195, 'change_count': 327, 'confidence': 0.2, 'kb': 'KB5065426'} +-------------------------------------------------------------------- +CVE-2025-55228 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +win32kbase.sys – process life-cycle helpers (UserProcessDestroyCallout, +xxxUserProcessInitCallout) and Win32JobObject helpers (AddProcess, +Terminated) that manipulate per-session Win32K data structures. + +Vulnerability Class +-------------------------------------------------------------------- +CWE-362: Race condition due to missing locking +CWE-416: Use-after-free on session / job lists as a consequence + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +Several callout helpers that run in the context of process creation, +process teardown and job-object bookkeeping were compiled into trivial +inline wrappers that only touched raw session pointers: + + * UserProcessDestroyCallout wrote a supplied pointer straight into the + UserSessionState +0x00 without taking the session critical section. + * xxxUserProcessInitCallout only compared a field at UserSessionState + +0x10 without any serialisation. + * Win32JobObject::AddProcess and …::Terminated simply read or wrote + session fields (+0x00, +0x08) without protecting the shared lists + that track W32PROCESS / W32JOB objects. + +Because these helpers run on arbitrary kernel worker threads while the +GUI subsystem is still live, multiple threads could enter them in +parallel for the same session. At the same time other paths (e.g. +NtUserCreateWindowStation, graphics IOCTLs, DComp) legitimately assumed +that the session-global lists are protected by the "User crit" lock. + +The result is a classic TOCTOU race: + +1. Thread A frees or re-initialises an entry in the session array + (e.g., destroys a W32PROCESS record). +2. Before the pointer is cleared, Thread B dereferences it from the same + array slot because no lock is held. +3. The freed memory can now be used as a dangling object and its vtable + or function pointers are executed under kernel context, yielding + arbitrary code execution (R0) within win32kbase.sys. + +The window for the race is small but repeatable because an attacker can +create and destroy large numbers of GUI processes or job objects across +several CPUs, forcing overlap between the unsynchronised assignments and +other GUI operations. + +Structures/fields involved (offsets inside UserSessionState): + +0x00 gpresUser – pointer to session user crit owner + +0x08 gpresRender – pointer to render crit owner + +0x10 gpresMitRitHazard – pointer used by RIT mitigation logic + +0x4CE8 etc. deferred unlock lists + +All accesses above were performed without acquiring the corresponding +_FAS_RESOURCE * user critical section, breaking the contract expected by +core win32k code. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +Before patch (UserProcessDestroyCallout prototype): +```c +__int64 *__fastcall SGCRITTYPEgpresUser<_FAST_ERESOURCE *>::operator=( + __int64 a1, __int64 *a2) +{ + __int64 v2 = *a2; + *(_QWORD *)W32GetUserSessionState(a1) = v2; // unsynchronised write + return a2; +} +``` + +After patch: +```c +void __fastcall UserProcessDestroyCallout(_W32PROCESS *proc) +{ + __int64 s = W32GetUserSessionState(proc); + __int64 ctx = UserCritInternal::EnterCritInternal(s, 0); + *(_QWORD *)(s + 0x18) = ctx; // owner tracking + ... // validate context + DestroySharedUserCritDeferredUnlockList(s+0x4CE8); + ... // safe cleanup + UserSessionSwitchLeaveCritWithNonPaged(0); +} +``` +Similar locking and validation blocks were added to xxxUserProcessInit +and both Win32JobObject helpers. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker spawns two or more threads. +2. Thread A repeatedly creates GUI processes and immediately exits them + so that UserProcessDestroyCallout is invoked. +3. Thread B simultaneously issues win32k system calls that walk the + session job / process lists (e.g., NtUserBuildSelectionArray or job + UI restriction APIs). +4. Because the destroy path writes a freed pointer without a lock, + Thread B dereferences freed memory, leading to control-flow hijack. + +Attack Vector +-------------------------------------------------------------------- +Local, authenticated. Requires the ability to create GUI processes or +job objects inside the same session – achievable from any normal user +account. + +Patch Description +-------------------------------------------------------------------- +The patch converts the stubbed template operators into full +implementations that: + • Call UserCritInternal::EnterCritInternal(Ex) to acquire the per + session _FAST_ERESOURCE ("user crit") in exclusive mode. + • Mark the resulting GUI context as owned (byte +0x6AC). + • Validate IsValidGuiContext() and flush deferred unlock lists before + proceeding. + • Use AtomicExecutionCheck to defend against re-entrancy. + • On exit call UserSessionSwitchLeaveCritWithNonPaged() to release the + lock. + • RAII helper CTempW32ThreadNonPaged guarantees correct IRQL. +No functional change beyond proper synchronisation is introduced. + +Security Impact +-------------------------------------------------------------------- +Prior to the patch, an attacker could win a race that dereferences a +freed W32PROCESS/W32JOB pointer, enabling: + • Arbitrary kernel read/write via crafted fake objects. + • Escalation to ring-0 or sandbox escape. + • Potential system compromise when chained with a GUI-trigger delivered + from remote vectors (RDP, browser, etc.). + +Fix Effectiveness +-------------------------------------------------------------------- +The updated code consistently acquires the session critical section +before touching shared state, closing the race window. All subsequent +writes/reads occur while holding the lock, and deferred unlock lists are +flushed under the same protection. No remaining unlocked accesses to +UserSessionState+[0,8,10] were found in the modified functions, +indicating the vulnerability is effectively remediated. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srv.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srv.sys.txt new file mode 100644 index 0000000..458931e --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srv.sys.txt @@ -0,0 +1,140 @@ +{'patch_store_uid': '8796b9a4-2f21-4dfb-9ac8-ac90635d192f', 'date': 1757843386.7100344, 'change_count': 16, 'confidence': 0.11, 'cve': 'CVE-2025-55234', 'kb': 'KB5065426', 'file': 'srv.sys'} +-------------------------------------------------------------------- +CVE-2025-55234 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Windows kernel driver srv.sys (SMB server). All affected logic is in +its legacy SMB-1 signing and HMAC helpers ( CngRsa32Compat_* / MD5* / +HMACMD5* ) and in code that verifies or generates SMB security +signatures during SessionSetup, read/write and pipe traffic. + + +Vulnerability Class +-------------------------------------------------------------------- +Improper Authentication / Weak Cryptography (CWE-287, CWE-327). +A home-grown MD5/HMAC-MD5 implementation inside the kernel allowed +creation and verification of SMB signatures that are vulnerable to +collision and relay attacks, resulting in elevation of privilege. + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +1. Signing context allocation + • For every connection the driver keeps a 0x68-byte structure at + Connection+0xB8 ( see SrvInitializeSmbSecuritySignature ). + • Before patch the structure was filled by the driver’s own + MD5Init / HMACMD5Init routines which merely wrote literal MD5 + IV constants (0x67452301, ‑0x10325477 …) and left the rest of + the buffer uninitialised. + +2. Update / Final + • MD5Update() is a 140-line hand-rolled implementation that reads + and writes through pointer arithmetic with no parameter + validation. When it is called from SrvAddSmbSecuritySignature + or SrvCheckSmbSecuritySignature the packet length (a3) is taken + directly from untrusted SMB headers. An attacker can supply + values that cause integer wrap-arounds in + v4 = 8 * a3 + *a1; + and later mem-copies, leading to out-of-bounds reads / writes + in kernel memory and, more importantly, to a predictable hash + state. + +3. Verification logic + • SrvCheckSmbSecuritySignature copied the whole internal state + (7 * 0x10 bytes) from another connection object and continued + hashing new network data with it, making signatures re-usable. + • Only the final 8 bytes of the MD5 digest were compared to the + network value. A single MD5 collision therefore lets an + attacker forge a valid signature with negligible effort. + +4. Consequence + • With signing considered valid the server trusts the session + setup, accepts any presented UID and treats the connection as + authenticated. The attacker can then relay NTLM credentials or + mount IPC$ as an elevated user (EoP). + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// before +void __fastcall MD5Init(_DWORD *a1) { + *a1=0; a1[1]=0; a1[2]=0x67452301; a1[3]=0xEFCDAB89; + a1[4]=0x98BADCFE; a1[5]=0x10325476; // no status returned +} +char __fastcall MD5Update(unsigned int *ctx,_QWORD *buf,__int64 len){ + v4 = 8*len + *ctx; // may wrap -> wrong length + ... // hundreds of unchecked mem-copies +} + +// patched +NTSTATUS __fastcall CngRsa32Compat_MD5Init(BCRYPT_HASH_HANDLE *ph){ + NTSTATUS s = BCryptCreateHash(0x21,ph,0,0,0,0,0); + if (s<0) __fastfail(7); + return s; +} +NTSTATUS __fastcall CngRsa32Compat_SHA256Update( + BCRYPT_HASH_HANDLE *ph,UCHAR *p,ULONG cb){ + NTSTATUS s = BCryptHashData(*ph,p,cb,0); + if (s<0) __fastfail(7); + return s; // no manual maths any more +} +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker connects with SMB 1 dialect. +2. Sends crafted SessionSetupAndX (BlockingSessionSetupAndX): + • sets Flags2 to disable signing requirements + • supplies oversized ByteCount to MD5Update. +3. SrvAddSmbSecuritySignature copies foreign hash state, + processes attacker-controlled data with vulnerable MD5Update and + stores only 64 bits of the digest. +4. Comparison in SrvCheckSmbSecuritySignature succeeds; connection is + marked as authenticated (Conn+0xB3 = 1). +5. Attacker now issues tree-connect / NT create requests under SYSTEM. + + +Attack Vector +-------------------------------------------------------------------- +Remote, unauthenticated network access to TCP/445 (SMB 1 enabled). No +local privilege is required; the attack works in default domain +configurations where signing is negotiated but not required. + + +Patch Description +-------------------------------------------------------------------- +• All custom MD5 / HMAC-MD5 code paths removed and replaced by CNG + primitives (BCryptCreateHash / BCryptHashData / BCryptFinishHash). +• Hash algorithm switched from MD5 to SHA-256. +• Every helper now returns NTSTATUS and terminates the kernel thread + with FAST_FAIL on error. +• SrvAddSmbSecuritySignature & SrvCheckSmbSecuritySignature now use + BCryptDuplicateHash instead of copying raw context memory. +• 8-byte comparison replaced by full SHA-256 comparison. +• WIL / feature-logging helpers updated so that audit events can be + emitted (new CVE audit ID 0x0379C624). + + +Security Impact +-------------------------------------------------------------------- +Before the patch an attacker could +1. Forge or bypass SMB 1 signatures, +2. Relay NTLM credentials, and +3. Obtain SYSTEM privileges on the target machine. +The issue therefore constitutes an Elevation of Privilege in the SMB +server and enables cross-machine relay attacks. + + +Fix Effectiveness +-------------------------------------------------------------------- +The vulnerable MD5 code is completely removed; all operations are now +performed by vetted CNG providers that internally validate lengths and +states and use SHA-256, eliminating collision and length-overflow +issues. State cloning is no longer possible because only opaque +BCrypt handles are stored. FAST_FAIL guards ensure any unexpected +error crashes the offending thread instead of proceeding with an +insecure state. The patch is therefore considered effective. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srvnet.sys.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srvnet.sys.txt new file mode 100644 index 0000000..251fc2b --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srvnet.sys.txt @@ -0,0 +1,129 @@ +{'kb': 'KB5065426', 'confidence': 0.17, 'date': 1757843360.7339995, 'file': 'srvnet.sys', 'patch_store_uid': '09a804f6-2a15-4428-99b8-410315cedbed', 'cve': 'CVE-2025-55234', 'change_count': 9} +-------------------------------------------------------------------- +CVE-2025-55234 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft SMB server kernel driver (srvnet.sys), routines dealing with +Service-Principal-Name (SPN) validation during administrative session +setup: + • SrvAdminValidateSpn() + • SrvNetRefreshLanmanServerParameters() + + +Vulnerability Class +-------------------------------------------------------------------- +Stack-based buffer overflow / improper parameter validation (CWE-787 +/ overlaps CWE-287 because the overwrite lets an attacker bypass SPN +validation, ending in improper authentication). + + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +SrvAdminValidateSpn() is responsible for evaluating the negotiated +security context and determining whether the remote client supplied a +valid SPN when attempting privileged operations over SMB. The helper +function SrvAdminCheckSpn() returns two flag values: + • AdminSpnIsPresent (1 byte) + • ClientSpnIsPresent (3-byte array, index-0 used as a flag) + +The original implementation called the helper with the last two +parameters swapped: + SrvAdminCheckSpn( SpnString, FlagArray3, &Flag1Byte ); +Here FlagArray3 points at a 3-byte local (char v23[3]) while &Flag1Byte +points at a single byte (unsigned char v22). + +SrvAdminCheckSpn() actually expects the opposite order + (SpnString, BYTE *OneByteFlag, CHAR[3] Buffer) +so it writes up to three bytes through the third argument. Because the +function received a pointer to only **one** byte, two bytes of stack +memory directly following v22 were silently overwritten. The smashed +bytes belong to other in-scope locals (including control/status values) +and can be influenced by an attacker through crafted authentication data +(the content of the SPN). + +Corrupting those locals allows the caller to falsify the outcome of the +SPN checks (e.g., set v22 or v23[0] to zero/non-zero at will) and bypass +authentication hardening that protects against credential-relay +(elevation of privilege) attacks. Although the overwrite is small (2 +bytes), it targets security-critical flags living on a protected kernel +stack frame, thereby undermining the decision logic that ultimately sets +*out* parameters a5/a6 returned to higher layers. + + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// vulnerable call – before patch +unsigned __int8 v22; // 1-byte flag +char v23[3]; // 3-byte buffer +... +SrvAdminCheckSpn(v26, v23, &v22); // parameters swapped +``` + +```c +// fixed call – after patch +unsigned __int8 v22; // 1-byte flag +char v23[3]; +... +SrvAdminCheckSpn(v26, &v22, v23); // correct order +``` + + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Remote attacker performs SMB session setup that reaches the + administrative pipe path. +2. SMB server enters SrvAdminValidateSpn() during authentication. +3. Crafted security context forces SrvAdminCheckSpn() to populate all + three bytes of its third argument. +4. Because that third argument is actually a 1-byte variable, two bytes + of the kernel stack are overwritten. +5. Overwritten bytes include decision flags; attacker can clear the + "SPN required" indicators. +6. Function returns success even though no valid SPN was supplied, + enabling relay-style elevation of privilege. + + +Attack Vector +-------------------------------------------------------------------- +Any network attacker able to negotiate an SMB session to a vulnerable +Windows server can send manipulated authentication data (NTLM / +Negotiate) that drives the SPN-validation path. No credentials are +required; only the ability to reach the SMB service. + + +Patch Description +-------------------------------------------------------------------- +1. Corrected the parameter order in the SrvAdminCheckSpn() invocation, + ensuring a 3-byte buffer is supplied where the callee expects it. +2. Adjusted local types (v26 is now a _WORD* instead of an int64) to + match the real data layout and avoid future mismatches. +3. Added an extra argument to SrvAdminValidateSpn() and extensive ETW + logging/auditing (AuditClientSpnSupport) so administrators can detect + devices that would fail once strict SPN/EPA rules are enabled. +4. SrvNetRefreshLanmanServerParameters() now parses the new registry key + AuditClientSpnSupport and propagates the global boolean + SrvNetAuditClientSpnSupport so that the auditing code can be turned + on or off. + + +Security Impact +-------------------------------------------------------------------- +Prior to the update an unauthenticated remote attacker could +programmatically corrupt kernel stack memory inside srvnet.sys and, +importantly, bypass SPN enforcement. This allowed credential-relay and +other elevation-of-privilege scenarios against servers that had not yet +mandated SMB signing or EPA. The attack requires only network access to +the SMB port (TCP/445). + + +Fix Effectiveness +-------------------------------------------------------------------- +Supplying the correct buffer sizes removes the overwrite, thereby +restoring reliable SPN validation. Additional auditing paths introduce +no new writable buffers. Static inspection shows the 3-byte buffer now +points to v23 (size-correct) and the single-byte flag to v22, eliminating +stack corruption. No alternative path still uses the old prototype, so +the patch fully addresses the described flaw. diff --git a/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srvsvc.dll.txt b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srvsvc.dll.txt new file mode 100644 index 0000000..6bc0278 --- /dev/null +++ b/reports/2025-sep-12390-windows_11_24H2_x64/CVE-2025-55234_srvsvc.dll.txt @@ -0,0 +1,126 @@ +{'file': 'srvsvc.dll', 'kb': 'KB5065426', 'date': 1757843382.2691083, 'confidence': 0.11, 'change_count': 2, 'patch_store_uid': 'e42d231a-805d-4cbb-a870-82678a532ac8', 'cve': 'CVE-2025-55234'} +-------------------------------------------------------------------- +CVE-2025-55234 Report +-------------------------------------------------------------------- + +Component +-------------------------------------------------------------------- +Microsoft Windows – srvsvc.dll (SMB Server service) +‣ Function 1 : ClCertValidateAce +‣ Function 2 : SsScavengerThread (indirectly affected) +Both routines are part of the kernel-mode SMB server that evaluates +certificate-based access-control entries (CAC) and controls SMB server +run-time behaviour. + +Vulnerability Class +-------------------------------------------------------------------- +Improper Authentication / Input-validation bypass (CWE-287) + +Detailed Root Cause Analysis +-------------------------------------------------------------------- +ClCertValidateAce is responsible for validating a certificate hash that +is supplied in an SMB certificate-ACE stored under + HKLM\System\CurrentControlSet\Services\LanmanServer\Certs + +1. The routine first checks that the caller-supplied string length + matches the expected hash digest length returned by + BCryptGetProperty("HashDigestLength"). + +2. Prior to the patch, the only syntactic validation of the hash + string was the call + if (!ClCertIsHexString(a4)) { error 87; } + +3. ClCertIsHexString was an internal helper that attempts to decide + whether every character of the thumb-print is a hexadecimal digit. + Its exact implementation is not shown in the diff, but the fix makes + it clear that the helper is defective: it can declare many non-hex + characters as valid (e.g. control, spacing or extended-ASCII + characters). Consequently the function accepted malformed thumbprint + strings as long as the overall string length happened to be twice the + digest size. + +4. Because the malformed string is accepted, the ACE is regarded as + syntactically correct and the SMB server will subsequently attempt to + match client certificates against the bogus thumb-print. + Depending on the character substitutions an attacker can generate a + value that matches multiple distinct digests or can be coerced to an + all-zero/known value after the later binary conversion. This allows + an attacker who controls the registry to create an ACE that matches + any certificate, bypassing the protection that CAC is supposed to + provide. + +5. The bypass lets a local or remote relay attacker gain the privilege + associated with the affected certificate filter – effectively an + elevation of privilege inside the SMB server. + +6. The companion changes in SsScavengerThread only re-point static + handles and trace GUIDs; they are housekeeping and not part of the + root cause. + +Vulnerability Code Snippets +-------------------------------------------------------------------- +```c +// Old (vulnerable) +ClCertDestroyHashHandle(hObject); +if (!(unsigned __int8)ClCertIsHexString(a4)) +{ + CanonicalDistinguishedName = 87; // ERROR_INVALID_PARAMETER + ... +} + +// New (fixed) +ClCertDestroyHashHandle(hObject); +for (j = 0; a4[j]; ++j) +{ + if (!(unsigned)_o_iswxdigit(a4[j])) + { + ValueW = 87; // reject on first non-hex digit + ... + } +} +``` +The patch inlines a strict per-character test using the CRT wide-character +helper _iswxdigit, completely bypassing the old, faulty helper. + +Trigger Flow (Top-Down) +-------------------------------------------------------------------- +1. Attacker writes a crafted registry value under + …\LanmanServer\Certs that contains an invalid thumb-print string. +2. SMB server reads the registry entry and calls ClCertValidateAce during + its periodic scavenger thread or during startup. +3. Defective ClCertIsHexString mis-classifies the string as valid. +4. ACE is accepted; subsequent certificate checks are performed against + the attacker-controlled (and effectively wildcard) value. + +Attack Vector +-------------------------------------------------------------------- +The attacker must be able to create or modify the registry value +mentioned above. This can be achieved by: +• Local privilege with write access to the key, or +• A remote relay attack that leverages another service running under a + SYSTEM context that is permitted to update the key. +Once the malicious ACE is installed, any SMB client authentication that +relies on certificate filtering can be bypassed. + +Patch Description +-------------------------------------------------------------------- +1. Removed use of ClCertIsHexString. +2. Added explicit loop that verifies every character with + _iswxdigit, guaranteeing strict hexadecimal compliance. +3. Minor clean-ups (variable renaming, trace-GUID replacement and event + handle renumbering) are not security-relevant. + +Security Impact +-------------------------------------------------------------------- +Prior to the fix, a malformed but length-correct certificate thumbprint +string was accepted as valid. This lets an attacker craft an ACE that +matches arbitrary or multiple certificates, nullifying SMB certificate +access control and enabling elevation-of-privilege relay attacks. + +Fix Effectiveness +-------------------------------------------------------------------- +The new code checks each character individually and aborts on the first +non-hex digit. Because the decision is now made inside the same source +file (and not in an opaque helper), the flaw is eliminated and cannot be +re-introduced without altering this explicit loop. No residual bypass +has been identified in the patched logic. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8c2cea8 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,23 @@ +azure-core==1.34.0 +azure-identity==1.23.0 +chromadb==1.0.9 +langchain-chroma==0.2.4 +lxml_html_clean==0.4.2 +requests-html==0.10.0 +pefile==2024.8.26 +langchain==0.3.25 +langchain-chroma==0.2.4 +langchain-community==0.3.24 +langchain-core==0.3.80 +langchain-mcp-adapters==0.1.7 +langchain-openai==0.3.16 +langchain-text-splitters==0.3.8 +langgraph==0.4.5 +langgraph-checkpoint==2.0.26 +langgraph-prebuilt==0.2.0 +langgraph-sdk==0.1.70 +langsmith==0.3.42 +polars==1.29.0 +python-bindiff==0.3.1 +python-binexport==0.3.7 +posthog==4.0.1 \ No newline at end of file diff --git a/rsrc/get_cached.gif b/rsrc/get_cached.gif new file mode 100644 index 0000000..da94fb1 Binary files /dev/null and b/rsrc/get_cached.gif differ diff --git a/rsrc/kev_stats.py b/rsrc/kev_stats.py new file mode 100644 index 0000000..1103d69 --- /dev/null +++ b/rsrc/kev_stats.py @@ -0,0 +1,99 @@ +import os, sys, time, math, requests +from datetime import datetime, timezone + +BASE = "https://services.nvd.nist.gov/rest/json/cves/2.0?hasKev" +HDR = {"User-Agent": "tte/1.0", "Accept": "application/json"} +if os.getenv("NVD_API_KEY"): HDR["apiKey"] = os.getenv("NVD_API_KEY") + +# ---- hard-coded date range (UTC) ---- +y = 2018 +START_DATE = datetime(y, 1, 1, tzinfo=timezone.utc) +END_DATE = datetime(y+1, 1, 1, tzinfo=timezone.utc) + + +# ------------------------------------- + +def pdt(s): + if not s: return None + s = s.strip().replace("Z", "+00:00") + fmts = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d"] + for f in fmts: + try: + dt = datetime.strptime(s.split("+")[0], f) + if "T" not in f and dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) + return dt.replace(tzinfo=timezone.utc) + except: + pass + try: + dt = datetime.fromisoformat(s) + if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) + return dt.astimezone(timezone.utc) + except: + return None + + +def fetch(): + out = []; + start = 0 + while True: + r = requests.get(BASE, headers=HDR, timeout=30) + if r.status_code != 200: raise SystemExit(f"HTTP {r.status_code}: {r.text[:200]}") + j = r.json(); + vs = j.get("vulnerabilities", []) + if not vs: break + out.extend(vs); + start += j.get("resultsPerPage", len(vs)) + if start >= j.get("totalResults", start): break + time.sleep(0.6) + return out + + +def hist(deltas, edges): + counts = [0] * (len(edges) - 1) + for d in deltas: + for i in range(len(edges) - 1): + if edges[i] <= d < edges[i + 1]: counts[i] += 1; break + return counts + + +def main(): + vulns = fetch() + deltas, zero, neg = [], [], [] + for e in vulns: + c = e.get("cve", {}) + pub = pdt(c.get("published")) + if not pub or not (START_DATE <= pub <= END_DATE): continue # filter by date range + add = pdt(c.get("cisaExploitAdd") or c.get("cisaExploitAdded")) + if pub and add: + dd = int(math.floor((add - pub).total_seconds() / 86400)) + deltas.append(dd) + if dd == 0: zero.append((c.get("id"), c.get("published"), c.get("cisaExploitAdd"))) + if dd < 0: neg.append((c.get("id"), c.get("published"), c.get("cisaExploitAdd"), dd)) + if not deltas: raise SystemExit("No parsed pairs in date range.") + edges = [-36500, -30, -7, -1, 0, 1, 7, 30, 90, 365, 36500] + counts = hist(deltas, edges) + total = len(deltas) + print(f"Filtered date range: {START_DATE.date()} – {END_DATE.date()}") + print("Time-to-Exploit (days) = cisaExploitAdd - published") + print(f"Parsed pairs: {total}") + print( + f"Zero-day: {len(zero)} ({len(zero) / total * 100:.2f}%) Negative: {len(neg)} ({len(neg) / total * 100:.2f}%)") + srt = sorted(deltas); + med = srt[total // 2] if total % 2 else (srt[total // 2 - 1] + srt[total // 2]) // 2 + print(f"Median: {med} Mean: {sum(deltas) / total:.2f}") + print("\nHistogram:") + mx = max(counts); + w = 50 if mx > 50 else mx + for a, b, cnt in zip(edges[:-1], edges[1:], counts): + label = f"[{a},{b})".rjust(15) + bar = "#" * (0 if mx == 0 else max(1, int(cnt * w / mx))) + print(f"{label} {str(cnt).rjust(6)} {bar}") + if zero: + print("\nExamples zero-day:") + for r in zero[:10]: print(" ", r[0], r[1], r[2]) + if neg: + print("\nExamples negative:") + for r in neg[:10]: print(" ", r[0], r[1], r[2], r[3]) + + +if __name__ == "__main__": main() diff --git a/rsrc/single_analysis.gif b/rsrc/single_analysis.gif new file mode 100644 index 0000000..18753c7 Binary files /dev/null and b/rsrc/single_analysis.gif differ diff --git a/supervisor/__init__.py b/supervisor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/supervisor/assistant.py b/supervisor/assistant.py new file mode 100644 index 0000000..d0f43c3 --- /dev/null +++ b/supervisor/assistant.py @@ -0,0 +1,137 @@ +import asyncio +import operator +import pickle +import threading +import uuid +from asyncio import Semaphore +from typing import Annotated + +import polars as pl +from pathlib import Path + +from langchain_core.documents import Document +from langchain_core.messages import SystemMessage +from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate +from langgraph.graph import StateGraph +from agent import Agent +from agent_tools.vector_store import VectorStore +from common import StateInfo, CveDetails, logger, Timer, LLM, resource_lock, console +from defaultdataclass import defaultdataclass, field +from langgraph.constants import Send, END + +from patch_analysis.files_collection import get_winsxs_df, get_report, get_update_dataframe, filter_executables, \ + file_desc +from patch_extractor import extractor, patch_tools +from patch_downloader import kb_downloader, get_os_data, cve_enrichment + +file_info_update_mutex = threading.RLock() + + +@defaultdataclass +class PatchSources: + current: str | Path | pl.dataframe.DataFrame + previous: str | Path | pl.dataframe.DataFrame + base: str | Path | pl.dataframe.DataFrame + + +@defaultdataclass +class OsDetails: + name: str + id: int + arch: str + + +@defaultdataclass +class GatherInfoContext: + state_info: StateInfo + cve_details: CveDetails + os: OsDetails + KB: PatchSources + extracted: PatchSources + dataframes: PatchSources + filtered_dataframes: PatchSources + updates: Annotated[list[Document], operator.add] = field(default_factory=list) + + +@defaultdataclass +class GatherInfoContextOutput: + state_info: StateInfo + cve_details: CveDetails = field(default_factory=CveDetails) + os: OsDetails = field(default_factory=OsDetails) + KB: PatchSources = field(default_factory=PatchSources) + extracted: PatchSources = field(default_factory=PatchSources) + dataframes: PatchSources = field(default_factory=PatchSources) + filtered_dataframes: PatchSources = field(default_factory=PatchSources) + + +def load_delta_modules(kb): + extracted = kb.parent.resolve() / f'extracted_{kb.name}' + dd = extracted / 'DesktopDeployment.cab' / 'UpdateCompression.dll' + if dd.exists(): + patch_tools.PatchTools.load_delta_modules([str(dd.resolve())]) + + +@Timer() +async def extract_kbs(prev, curr): + tasks = [extractor.aextract(prev, None, False), + extractor.aextract(curr, None, False)] + + load_delta_modules(prev) + load_delta_modules(curr) + + await asyncio.gather(*tasks) + + +class Chatbot(Agent): + """ + This class represents an agent responsible for gathering information, facilitating + state management, and executing specific tasks such as retrieving CVE information, + downloading and extracting updates, and adding file information to the vecotrstore. + """ + semaphore: Semaphore = Semaphore(500) + + @defaultdataclass(frozen=True) + class NODES: + # get_info = "Get CVE information" + chatbot = "Chatbot" + + def __init__(self): + super().__init__(llm=LLM.nano) + + prompt_template = ChatPromptTemplate( + input_variables=["filename", "package", "description"], + messages=[SystemMessage('You are a senior Windows-internals analyst'), + HumanMessagePromptTemplate.from_template( + "Write a maximum of 80 token unformatted paragraph about the Windows executable " + "{filename} package: {package} description: {description}. Include only technical details about " + "its purpose in the system. Keep it short, consistent, and strictly one paragraph. " + "Do not repeat facts. Omit headings, bullets, and conjunctions; the output is " + "for embedding context.") + ]) + + def _build(self): + if self._graph: + return + + builder = StateGraph(GatherInfoContext) + + builder.add_node(self.NODES.chatbot, self.chatbot) + + builder.set_entry_point(self.NODES.chatbot) + # builder.add_edge(self.NODES.get_info, self.NODES.download) + builder.add_edge(self.NODES.download, self.NODES.index) + + builder.add_conditional_edges(self.NODES.index, self.add_file_info_if_needed, + [ + self.NODES.add_file_info, + self.NODES.update_vs, + ]) + + builder.add_edge(self.NODES.add_file_info, self.NODES.update_vs) + builder.set_finish_point(self.NODES.update_vs) + + self._graph = builder.compile() + + def chatbot(self, context: GatherInfoContext): + pass + diff --git a/supervisor/cve_info.py b/supervisor/cve_info.py new file mode 100644 index 0000000..00fef53 --- /dev/null +++ b/supervisor/cve_info.py @@ -0,0 +1,52 @@ +import pickle +from pathlib import Path + +from common import Timer, console +from patch_downloader import get_os_data, cve_enrichment +from supervisor.gather_info import GatherInfoContext + + +@Timer() +def get_os_cvrf_data() -> tuple[str, int]: + cache_os = Path('db/.os') + + if cache_os.exists(): + os_data = pickle.loads(cache_os.read_bytes()) + else: + os_data = get_os_data.get_cvrf_product_name_and_id() + cache_os.write_bytes(pickle.dumps(os_data)) + + return os_data + + +@Timer() +def get_articles(context: GatherInfoContext): + # TODO: Cache to disk + metadata = cve_enrichment.report(context.cve_details.cve) + + articles = [] + product = next(filter(lambda x: x.get('productId') == context.os.id, metadata.products), None) + if not product: + # The CVE is not related to the provided os + console.warning(f'[-] The product {context.os.name} is not affected by {context.cve_details.cve}') + raise RuntimeError(f'The product {context.os.name} is not affected by {context.cve_details.cve}') + + for article in filter(lambda x: x.get('article') and x.get('supercedence'), product.get('articles')): + articles.append(article) + + if not articles: + # There is no 2 consecutive KBs + # TODO: handle first update after feature release + raise RuntimeError('Cannot find the CVE articles') + + article = next(filter(lambda x: x.get('type') == 'security update', articles), None) + if article is None: + article = articles[0] + + context.KB.base = product.get('baseVersion') + context.KB.current = 'KB' + article.get('article') + context.KB.previous = 'KB' + article.get('supercedence') + context.cve_details.description = metadata.description + context.cve_details.msrc_report = metadata + + console.info(f'[+] Get {context.cve_details.cve} articles, patched in {context.KB.current} and replace {context.KB.previous}.') diff --git a/supervisor/gather_info.py b/supervisor/gather_info.py new file mode 100644 index 0000000..3945377 --- /dev/null +++ b/supervisor/gather_info.py @@ -0,0 +1,306 @@ +import asyncio +import operator +import pickle +import threading +import uuid +from asyncio import Semaphore +from typing import Annotated + +import polars as pl +from pathlib import Path + +from langchain_core.documents import Document +from langchain_core.messages import SystemMessage +from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate +from langgraph.graph import StateGraph +from agent import Agent +from agent_tools.vector_store import VectorStore +from common import StateInfo, CveDetails, logger, Timer, LLM, resource_lock, console +from defaultdataclass import defaultdataclass, field +from langgraph.constants import Send, END + +from patch_analysis.files_collection import get_winsxs_df, get_report, get_update_dataframe, filter_executables, \ + file_desc +from patch_extractor import extractor, patch_tools +from patch_downloader import kb_downloader, get_os_data, cve_enrichment + +file_info_update_mutex = threading.RLock() + + +@defaultdataclass +class PatchSources: + current: str | Path | pl.dataframe.DataFrame + previous: str | Path | pl.dataframe.DataFrame + base: str | Path | pl.dataframe.DataFrame + + +@defaultdataclass +class OsDetails: + name: str + id: int + arch: str + + +@defaultdataclass +class GatherInfoContext: + state_info: StateInfo + cve_details: CveDetails + os: OsDetails + KB: PatchSources + extracted: PatchSources + dataframes: PatchSources + filtered_dataframes: PatchSources + updates: Annotated[list[Document], operator.add] = field(default_factory=list) + + +@defaultdataclass +class GatherInfoContextOutput: + state_info: StateInfo + cve_details: CveDetails = field(default_factory=CveDetails) + os: OsDetails = field(default_factory=OsDetails) + KB: PatchSources = field(default_factory=PatchSources) + extracted: PatchSources = field(default_factory=PatchSources) + dataframes: PatchSources = field(default_factory=PatchSources) + filtered_dataframes: PatchSources = field(default_factory=PatchSources) + + +def load_delta_modules(kb): + extracted = kb.parent.resolve() / f'extracted_{kb.name}' + dd = extracted / 'DesktopDeployment.cab' / 'UpdateCompression.dll' + if dd.exists(): + patch_tools.PatchTools.load_delta_modules([str(dd.resolve())]) + + +@Timer() +async def extract_kbs(prev, curr): + tasks = [extractor.aextract(prev, None, False), + extractor.aextract(curr, None, False)] + + load_delta_modules(prev) + load_delta_modules(curr) + + await asyncio.gather(*tasks) + + +class GatherInfo(Agent): + """ + This class represents an agent responsible for gathering information, facilitating + state management, and executing specific tasks such as retrieving CVE information, + downloading and extracting updates, and adding file information to the vecotrstore. + """ + semaphore: Semaphore = Semaphore(500) + + @defaultdataclass(frozen=True) + class NODES: + # get_info = "Get CVE information" + download = "Download & extract updates" + index = "Indexing" + add_file_info = "Add to file info store" + update_vs = "Update vecotrstore" + + def __init__(self): + super().__init__(llm=LLM.nano) + + prompt_template = ChatPromptTemplate( + input_variables=["filename", "package", "description"], + messages=[SystemMessage('You are a senior Windows-internals analyst'), + HumanMessagePromptTemplate.from_template( + "Write a maximum of 80 token unformatted paragraph about the Windows executable " + "{filename} package: {package} description: {description}. Include only technical details about " + "its purpose in the system. Keep it short, consistent, and strictly one paragraph. " + "Do not repeat facts. Omit headings, bullets, and conjunctions; the output is " + "for embedding context.") + ]) + + def _build(self): + if self._graph: + return + + builder = StateGraph(GatherInfoContext) + + # builder.add_node(self.NODES.get_info, self.get_info) + builder.add_node(self.NODES.download, self.download_and_extract_updates) + builder.add_node(self.NODES.index, self.index) + builder.add_node(self.NODES.add_file_info, self.add_file_info) + builder.add_node(self.NODES.update_vs, self.update_vector_store) + + builder.set_entry_point(self.NODES.download) + # builder.add_edge(self.NODES.get_info, self.NODES.download) + builder.add_edge(self.NODES.download, self.NODES.index) + + builder.add_conditional_edges(self.NODES.index, self.add_file_info_if_needed, + [ + self.NODES.add_file_info, + self.NODES.update_vs, + ]) + + builder.add_edge(self.NODES.add_file_info, self.NODES.update_vs) + builder.set_finish_point(self.NODES.update_vs) + + self._graph = builder.compile() + + @staticmethod + def get_update_set(context: GatherInfoContext): + update_set = set() + exists_set = set() + + for row in context.filtered_dataframes.base.unique(subset=['name', 'package']).iter_rows(named=True): + update_set.add((row['name'], row['package'])) + + file_info_collection = VectorStore.file_info.get() + for metadata in file_info_collection.get('metadatas'): + exists_set.add((metadata['name'], metadata['package'])) + + update_set = update_set - exists_set + + return update_set + + def add_file_info_if_needed(self, context: GatherInfoContext): + updates = [] + + update_set = self.get_update_set(context) + + if update_set: + for row in context.filtered_dataframes.base.unique(subset=['name', 'package']).iter_rows(named=True): + if (row['name'], row['package']) in update_set: + base = Path(row['path']).parents[1] / row['name'] + updates.append(Send(self.NODES.add_file_info, (base, row['name'], row['package']))) + + return updates or self.NODES.update_vs + + async def download_and_extract_updates(self, context: GatherInfoContext): + context.state_info.node.append(self.NODES.download) + + async def print_dots(): + try: + dots = "" + for i in range(50): + dots += "." + # Move cursor to beginning of line, clear it, and print progress + print(f"\rDownloading: {dots}", end="", flush=True) + await asyncio.sleep(0.1*i) + except asyncio.CancelledError: + pass + + dot_task = asyncio.create_task(print_dots()) + + with Timer('download KBs'): + curr_task = asyncio.create_task( + asyncio.to_thread(kb_downloader.download_kb, context.KB.current, + context.os.name, Path('_temp'), False) + ) + prev_task = asyncio.create_task( + asyncio.to_thread(kb_downloader.download_kb, context.KB.previous, + context.os.name, Path('_temp'), False) + ) + + # Wait for both to complete + curr, prev = await asyncio.gather(curr_task, prev_task, return_exceptions=True) + dot_task.cancel() + + if not (curr.exists() and prev.exists()): + raise RuntimeError('Downloading of update installation failed') + + with Timer('extract KBs'): + console.info('[*] Start KBs extraction') + await extract_kbs(prev, curr) + context.extracted.previous = prev.parent / ('extracted_' + prev.name) + context.extracted.current = curr.parent / ('extracted_' + curr.name) + + async def index(self, context: GatherInfoContext): + context.state_info.node.append(self.NODES.index) + + winsxs_df = get_winsxs_df(context.KB.base) + console.info('[+] WinSxS indexing completed') + arch = context.os.arch + + # TODO: Support more architectures + prev_report = get_report(context.extracted.previous / 'report.txt', arch) + curr_report = get_report(context.extracted.current / 'report.txt', arch) + + prev_df = get_update_dataframe(context.KB.previous, prev_report, + cache=context.extracted.previous / 'report.cache') + curr_df = get_update_dataframe(context.KB.current, curr_report, + cache=context.extracted.current / 'report.cache') + + winsxs_df = winsxs_df.filter(filter_executables) + + context.dataframes.previous = prev_df + context.dataframes.current = curr_df + context.dataframes.base = winsxs_df + + # Filter reverse patch files from the winsxs + r_patch = winsxs_df.filter(pl.col("arch").eq(arch) & pl.col("delta_type").eq("r")) + + # All the files in the current KB that was changed from the last KB + # and have a reverse patch in the WinSxS folder + curr_relevant_df = ( + curr_df.filter( + pl.col("arch").eq(arch) # filter the x64 only and unmodified files + & ~pl.col("hash").is_in(prev_df["hash"]) + ).join(r_patch.select("package", "pubkey", "arch").unique(), # Correlate with the winsxs folder + on=["package", "pubkey", "arch"], + how="semi", + ) + ) + + # Same for previous + prev_relevant_df = ( + prev_df.filter( + pl.col("arch").eq(arch) # filter the x64 only and unmodified files + & ~pl.col("hash").is_in(curr_df["hash"]) + ).join(r_patch.select("package", "pubkey", "arch").unique(), # Correlate with the winsxs folder + on=["package", "pubkey", "arch"], + how="semi", + ) + ) + + # Filter all matched reverse patches to current + # It's enough since current update include previous updates + # TODO: handle edge cases + relevant_r_patch_df = r_patch.join( + curr_relevant_df.select(["package", "pubkey", "arch"]).unique(), + on=["package", "pubkey", "arch"], + how="semi", + ) + + # Filter files names as well + context.filtered_dataframes.previous = prev_relevant_df.filter( + pl.col('name').str.to_lowercase().is_in( + relevant_r_patch_df.get_column('name').str.to_lowercase())) + + context.filtered_dataframes.current = curr_relevant_df.filter( + pl.col('name').str.to_lowercase().is_in( + relevant_r_patch_df.get_column('name').str.to_lowercase())) + + context.filtered_dataframes.base = relevant_r_patch_df + + console.info(f'[+] {context.KB.current} and {context.KB.previous} indexing completed') + + async def add_file_info(self, args: tuple): + base, name, package = args + # The lock is reentrant, so it will not affect a single graph + # execution, but will prevent file info db corruption. + with file_info_update_mutex: + async with self.semaphore: + res = VectorStore.file_info.get( + where={'$and': [{'name': name}, {'package': package}]}, + ) + + if res.get('ids'): + return + + console.info(f'[*] Adding {name} to file info store') + desc = file_desc(base) or '_' if base.exists() else '_' + + chain = self.prompt_template | self.get_llm() + result = await chain.ainvoke({"filename": name, 'package': package, "description": desc}) + logger.debug(result.content) + + doc = Document(page_content=result.content or '', + metadata={'name': name.lower(), 'package': package.lower(), 'description': desc.lower()}) + await VectorStore.file_info.aadd_documents(documents=[doc], ids=[str(uuid.uuid4())]) + + def update_vector_store(self, context: GatherInfoContext): + ''' Placeholder node ''' + context.state_info.node.append(self.NODES.update_vs) diff --git a/supervisor/supervisor.py b/supervisor/supervisor.py new file mode 100644 index 0000000..522448a --- /dev/null +++ b/supervisor/supervisor.py @@ -0,0 +1,408 @@ +# Supervisor +import operator +from pathlib import Path +from typing import Annotated + +import polars as pl +from langchain_core.messages import BaseMessage, HumanMessage, AIMessage +from langchain_core.runnables import RunnableConfig + +from langgraph.constants import END, Send + +from agent import Agent +from agent_tools.vector_store import VectorStore +from common import StateInfo, logger, get_patch_store_df, PatchStoreEntry, Artifact, Report, CveDetails, resource_lock, \ + Threshold, console, LLM, save_to_file +from defaultdataclass import defaultdataclass, field + +from langgraph.graph import StateGraph, add_messages + +from patch_analysis.patch_delta import patch_entry +from patch_downloader import get_os_data +from re_agent.revearse_engineer_agent import ReverseEngineering, ReverseEngineeringOutput, ReverseEngineeringContext +from supervisor.cve_info import get_os_cvrf_data, get_articles +from supervisor.gather_info import GatherInfo, GatherInfoContextOutput +from supervisor.windows_internals import WindowsInternals, WindowsInternalsOutput +from vr_agent.vulnerability_researcher_agent import VulnerabilityResearchOutput, VulnerabilityResearch, \ + VulnerabilityResearchContext + + +@defaultdataclass(slots=True) +class SupervisorContext(GatherInfoContextOutput, + WindowsInternalsOutput, + ReverseEngineeringOutput, + VulnerabilityResearchOutput): + cve_details: Annotated[CveDetails, lambda _, new: new] = field(default_factory=CveDetails) + messages: Annotated[list[BaseMessage], add_messages] = field(default_factory=list) + state_info: Annotated[StateInfo, lambda _, new: new] = field(default_factory=StateInfo) + artifacts: Annotated[list[Artifact], operator.add] = field(default_factory=list) + reports: Annotated[list[Report], operator.add] = field(default_factory=list) + action: str = '' + + +class Supervisor(Agent): + @defaultdataclass(frozen=True) + class NODES: + cve_info = "Get CVE information" + gather_info = "Gather Information" + wi_agent = "Windows Internals Agent" + re_agent = "Reverse Engineering Agent" + vr_agent = "Vulnerability Research Agent" + assistant = "Assistant" + # patch = "Patch candidates" + + def __init__(self): + self.gather_info = GatherInfo() + self.wi_agent = WindowsInternals() + self.re_agent = ReverseEngineering() + self.vr_agent = VulnerabilityResearch() + self.llm = LLM.o4_mini + super().__init__(draw=True) + + async def run(self, cve: str, config=None): + initial_state = SupervisorContext() + + initial_state.cve_details.cve = cve + initial_state.state_info.node.append('START') + + console.info(f'[*] Starting analysis of {cve}') + async for step in self._graph.astream_events(initial_state, config, subgraphs=True): + yield step + + # for node_name, state_dict in step.items(): + # if state_dict: + # # state = {node_name: SupervisorContext(**state_dict)} + # yield state_dict + # else: + # yield None + + def _build(self): + if self._graph: + return + + builder = StateGraph(SupervisorContext) + + # builder.add_node(self.NODES.patch, self.patch) + builder.add_node(self.NODES.assistant, self.assistant) + builder.add_node(self.NODES.cve_info, self.get_cve_info) + builder.add_node(self.NODES.gather_info, self.gather_info.get_graph()) + builder.add_node(self.NODES.wi_agent, self.wi_agent.get_graph()) + builder.add_node(self.NODES.re_agent, self.re_agent.get_graph()) + builder.add_node(self.NODES.vr_agent, self.vr_agent.get_graph()) + + builder.set_entry_point(self.NODES.cve_info) + builder.add_conditional_edges(self.NODES.cve_info, self.check_report_cache, + [ + self.NODES.assistant, + self.NODES.gather_info + ]) + builder.add_edge(self.NODES.gather_info, self.NODES.wi_agent) + builder.add_edge(self.NODES.wi_agent, self.NODES.assistant) + builder.add_conditional_edges(self.NODES.assistant, self.router, + [ + self.NODES.assistant, + self.NODES.gather_info, + self.NODES.wi_agent, + self.NODES.re_agent, + self.NODES.vr_agent, + # self.NODES.patch, + END + ]) + + # builder.add_conditional_edges(self.NODES.patch, self.router, + # [ + # self.NODES.assistant, + # self.NODES.re_agent, + # ]) + builder.add_edge(self.NODES.wi_agent, self.NODES.assistant) + builder.add_edge(self.NODES.re_agent, self.NODES.assistant) + builder.add_edge(self.NODES.vr_agent, self.NODES.assistant) + + builder.set_finish_point(self.NODES.assistant) + + # memory = MemorySaver() + self._graph = builder.compile() + + def check_report_cache(self, context: SupervisorContext): + console.info('[*] Check for cached reports') + docs = VectorStore.reports.get( + where={'cve': context.cve_details.cve}, + ) + + reports = [] + for i in range(len(docs.get('ids'))): + doc = docs.get('documents')[i] + metadata = docs.get('metadatas')[i] + reports.append(Report( + content=doc, + cve_details=CveDetails(cve=metadata.get('cve')), + artifact=Artifact(primary_file=PatchStoreEntry(name=metadata.get('file'), + kb=metadata.get('kb'), + uid=metadata.get('patch_store_uid'))), + confidence=metadata.get('confidence') + )) + + if reports: + context.reports.extend(reports) + return self.NODES.assistant + + return self.NODES.gather_info + + def get_cve_info(self, context: SupervisorContext): + context.state_info.node.append(self.NODES.cve_info) + + console.debug('[*] Getting CVRF OS name and ID of this machine') + context.os.name, context.os.id = get_os_cvrf_data() + context.os.arch = get_os_data.processor_arch_tokens( + { + 0: ("x86",), + 5: ("arm",), + 9: ("amd64",), + 12: ("arm64",), + } + )[0] + console.info(f'[+] Currently running on {context.os.name} with ID {context.os.id}') + + logger.info(f'[*] Retrieve the metadata of {context.cve_details.cve}') + get_articles(context) + logger.info( + f'[+] {context.cve_details.cve} - {context.cve_details.msrc_report.title}:\n' + f'{context.cve_details.msrc_report.description}\n' + f'patched in {context.KB.current} supercedence {context.KB.previous}') + + @staticmethod + def _patch_candidates(context: SupervisorContext, ranked_df: pl.DataFrame): + + filter_df = ( + (pl.col('name').str.to_lowercase().is_in( + ranked_df.get_column('name').str.to_lowercase().implode())) & + (pl.col('package').str.to_lowercase().is_in( + ranked_df.get_column('package').str.to_lowercase().implode())) + ) + + # At this point we got the candidates, and we want to see if they are + # relevant to the current update + subjects: pl.DataFrame = context.filtered_dataframes.current.filter(filter_df).join( + ranked_df.select(['name', 'similarity score', 'relevancy']), + on='name', + how='left').sort("relevancy", descending=True) + + if subjects.is_empty(): + # There is no changes to these files in the current update + return + + with resource_lock('update_patch_store'): + patch_store_df = get_patch_store_df() + + try: + results = [] + for row in subjects.iter_rows(named=True): + try: + base_entry, curr_entry, prev_entry = patch_entry(row, + context.KB.base, + context.KB.current, + context.KB.previous, + context.dataframes.previous, + context.filtered_dataframes.base) + patched = [x for x in [base_entry, curr_entry, prev_entry] if x] + if patched: + results.append(pl.DataFrame(patched)) + except BaseException as e: + console.warning(e) + + if results: + patch_store_df = pl.concat( + [patch_store_df, *results], how="vertical_relaxed" + ) + finally: + patch_store_df.serialize(Path("db/.patch_store_df"), format="binary") + with pl.Config(tbl_cols=-1, set_tbl_width_chars=300): + logger.debug(patch_store_df) + + return subjects + + def router(self, context: SupervisorContext, config: RunnableConfig): + + match context.state_info.node[-2]: + case self.NODES.assistant: + if context.action: + action = context.action + context.action = '' + + match action: + case "reanalyze": + return self.NODES.gather_info + + case self.vr_agent.NODES.generate: + if context.reports: + msg = f'[+] Generated {len(context.reports)} reports for {context.cve_details.cve}\n' + console.info(msg) + context.messages.append(AIMessage(content=msg)) + for r in context.reports: + context.messages.append(AIMessage(content=r.content)) + else: + msg = f'[-] Failed to generate report for {context.cve_details.cve}\n' + console.info(msg) + context.messages.append(AIMessage(content=msg)) + + return self.NODES.assistant + + case self.NODES.cve_info: + msg = f'[+] Found {len(context.reports)} reports of {context.cve_details.cve}' + console.info(msg) + context.messages.append(AIMessage(content=msg)) + for r in context.reports: + context.messages.append(AIMessage(content=r.content)) + return self.NODES.assistant + + case self.wi_agent.NODES.rank: + # From windows internals agent + if not context.candidates.results: + raise RuntimeError('There is no valid candidates, ' + 'need ask windows internals agent for new candidates') + + ranked_df = pl.DataFrame( + [{'name': doc.metadata.get('name'), + 'package': doc.metadata.get('package'), + 'similarity score': score, + 'relevancy': rank} for doc, score, rank in context.candidates.results]) + console.info(f'\n{context.cve_details.cve} ranked candidates:\n{ranked_df}') + + subjects = self._patch_candidates(context, ranked_df) + + th = config.get('configurable', {}).get('threshold', Threshold()) + subjects = subjects.filter(pl.col('relevancy') > th.candidates) + + if subjects is None: + raise RuntimeError('There is no valid subjects, ' + 'need ask windows internals agent for new candidates') + + patch_store_df = get_patch_store_df() + + targets = [] + for row in subjects.iter_rows(named=True): + patched_subjects = patch_store_df.filter((pl.col("name") == row.get("name")) & + (pl.col("package") == row.get("package")) & + (pl.col("arch") == row.get("arch")) & + pl.col("kb").is_in((context.KB.current, + context.KB.previous, + context.KB.base)) + ) + selected = [entry for entry in patched_subjects.iter_rows(named=True)] + + if len(selected) < 2: + logger.error(f'No valid subjects found in patch store for {row.get("name")}') + continue + + primary = next(filter(lambda x: x['kb'] == context.KB.current, selected), None) + if primary is None: + # If we couldn't patch the current, we cannot analyze the changes + # therefore, skip it gracefully. + continue + + secondary = (next(filter(lambda x: x['kb'] == context.KB.previous, selected), None) or + next(filter(lambda x: x['kb'] == context.KB.base, selected), None)) + + targets.append(Send(self.NODES.re_agent, ReverseEngineeringContext( + state_info=context.state_info, + primary_file=PatchStoreEntry().from_dict(primary, overwrite=True), + secondary_file=PatchStoreEntry().from_dict(secondary, overwrite=True), + ))) + + if targets: + return targets + else: + pass # refine windows internals + + case self.re_agent.NODES.decompile: + # From RE agent + targets = [] + for artifact in context.artifacts: + targets.append(Send(self.NODES.vr_agent, VulnerabilityResearchContext( + state_info=context.state_info, + artifact=artifact, + cve_details=context.cve_details + ))) + + if targets: + return targets + + return END + + def assistant(self, context: SupervisorContext, config: RunnableConfig): + context.state_info.node.append(self.NODES.assistant) + console.debug(context.state_info.node) # TODO: remove + + if (config.get('configurable', {}).get('interrupt', False) and + context.state_info.node[-2] == self.NODES.assistant): + while True: + user_input = input("\nYou: ").strip() + if not user_input: + continue + + if user_input == "exit": + break + + match user_input: + case "help": + console.info(f'[+] Available commands:\n' + f' exit: exit the program\n' + f' help: show this help message\n' + f' reports: show the generated reports\n' + f' save reports: save reports to files\n' + f' delete all reports: delete all cached reports\n' + f' reanalyze: reanalyze the current CVE\n' + f' o3: Change the assistant model to O3\n' + f' o4-mini: Change the assistant model to O4 mini\n' + ) + continue + + case "reports": + for r in context.reports: + console.info(f'[+] Report for {r.cve_details.cve}:\n' + f'{r.content}\n' + ) + + continue + + case "save reports": + docs = VectorStore.reports.get( + where={'cve': context.cve_details.cve}, + ) + if docs: + save_to_file(docs, path=Path.cwd()) + console.info(f'[+] Saved reports to {Path.cwd()}') + else: + console.info(f'[-] No reports found for {context.cve_details.cve}') + continue + + case "delete all reports": + if input("Are you sure? [y/N] ").lower().startswith("y"): + docs = VectorStore.reports.get( + where={'cve': context.cve_details.cve}, + ) + if docs: + VectorStore.reports.delete(ids=docs.get('ids')) + console.info(f'[+] Deleted all reports for {context.cve_details.cve}') + else: + console.info(f'[-] No reports found for {context.cve_details.cve}') + continue + + case "reanalyze": + return {'action': 'reanalyze'} + + case "o3": + self.llm = LLM.o3 + continue + + case "o4-mini": + self.llm = LLM.o4_mini + continue + + context.messages.append(HumanMessage(content=user_input)) + + res = self.llm.invoke(context.messages) + context.messages.append(res) + res.pretty_print() + + return {'action': ''} diff --git a/supervisor/windows_internals.py b/supervisor/windows_internals.py new file mode 100644 index 0000000..5c25d4a --- /dev/null +++ b/supervisor/windows_internals.py @@ -0,0 +1,183 @@ +import asyncio +import json + +from langchain_core.documents import Document +from langchain_core.messages import SystemMessage, HumanMessage +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langgraph.graph import StateGraph +from pydantic import BaseModel, Field + +from agent import Agent +from agent_tools.vector_store import VectorStore +from common import StateInfo, CveDetails, Candidates, LLM, logger, console +from defaultdataclass import defaultdataclass +from langgraph.constants import END + + +@defaultdataclass +class WindowsInternalsContext: + state_info: StateInfo + cve_details: CveDetails + query: str + docs: list[tuple[Document, float]] + + +@defaultdataclass +class WindowsInternalsOutput: + candidates: Candidates + + +@defaultdataclass +class PromptTemplate: + collect: ChatPromptTemplate + rank: ChatPromptTemplate + + +class FileScore(BaseModel): + file: str = Field(..., description="Filename including extension") + score: float = Field(..., description="Relevance score from 0.0 to 10.0, higher is more relevant") + # reason: str = Field(..., description="One sentence about the reason for this score") + + +class FileScoreList(BaseModel): + files: list[FileScore] + + +class Query(BaseModel): + query: str = Field(..., description="Query to use for similarity search in the vectorstore") + + +class WindowsInternals(Agent): + ''' + This agent has access to Windows executable files information + through the file info vectorstore. + It can find executables that could be related to a description + of flow, vulnerability, and other scenarios. + ''' + + prompt_template: PromptTemplate = PromptTemplate( + collect=ChatPromptTemplate([ + SystemMessage( + # ---- WHAT TO DO ------------------------------------------------- + "You receive a JSON object that pinpoints WHERE the bug was fixed " + "(driver / service / DLL / EXE name appears in the title or description).\n" + "Write ONE paragraph, <= 80 tokens, plain ASCII.\n" + "That paragraph must sound like a terse engineer-authored note for the " + "*ordinary* behaviour of the exact file that got patched.\n" + # ---- HOW TO DO IT ---------------------------------------------- + "Mandatory content:\n" + "1) The executable’s primary job (e.g. ‘copies log sectors into caller buffer’).\n" + "2) Key internal logic tied to that job (loops, size checks, pointer math, " + " registry access, IRP handling, etc.).\n" + "3) Main OS components or APIs it talks to (Mm, Io, ALPC, SrvNet…).\n" + "4) Its purpose to the wider system (transaction logging, credential caching, …).\n" + # ---- HARD BANS --------------------------------------------------- + "Never say: CVE, CVSS, CWE, ‘bug’, ‘vulnerability’, patch status, risk, exploit.\n" + "No headings, lists, or newlines. No code blocks. No fluffy adjectives.\n" + # ---- STYLE ------------------------------------------------------- + "Use present tense. Technical verbs only. Keep it punchy and factual." + ), + MessagesPlaceholder("json_metadata") + ]), + rank=ChatPromptTemplate.from_template( + """ + You are an expert in Windows OS and at evaluating document relevance. + + ORIGINAL QUERY: {query} + ORIGINAL JSON: {metadata} + + Below are documents retrieved from a vector store. Your task is to rerank them based on their relevance + to the original query and JSON data, assigning a score from 0.00 (completely irrelevant) to 10.00 (perfectly relevant). + + DOCUMENTS: + {files} + + Analyze each document carefully and determine how well it addresses the information needs implied by the query and JSON. + """ + )) + + @defaultdataclass(frozen=True) + class NODES: + collect = "Collect relevant files" + rank = 'Rank relevancy' + + def __init__(self): + super().__init__(llm=LLM.mini) + self.limit = 3 + + def _build(self): + if self._graph: + return + + builder = StateGraph(WindowsInternalsContext) + + builder.add_node(self.NODES.collect, self.collect) + builder.add_node(self.NODES.rank, self.rank) + + builder.set_entry_point(self.NODES.collect) + builder.add_edge(self.NODES.collect, self.NODES.rank) + + builder.add_conditional_edges(self.NODES.rank, self.refinement, + [ + self.NODES.rank, + END + ]) + + builder.set_finish_point(self.NODES.rank) + + self._graph = builder.compile() + + def refinement(self, context: WindowsInternalsOutput): + + if 'ok': + return END + + if 'not ok': + if self.limit: + self.limit -= 1 + return self.NODES.rank + + return END + + async def collect(self, context: WindowsInternalsContext): + context.state_info.node.append(self.NODES.collect) + console.info(f'[*] Searching for potential candidates') + query_chain = self.prompt_template.collect | self.get_llm().with_structured_output(Query) + metadata = json.dumps({k: v for k, v in context.cve_details.msrc_report.to_dict().items() if k != 'products'}) + + result: Query = await query_chain.ainvoke( + {'json_metadata': [HumanMessage(metadata)]}) + + docs = await VectorStore.file_info.asimilarity_search_with_score(result.query, k=10) # TODO: add config + logger.info(f'Found {len(docs)} docs for query "{result.query}"') + + if len(docs): + console.info(f'[+] Found {len(docs)} potential candidates for {context.cve_details.cve} (limit: 10)') + else: + console.info(f'[-] Failed to find candidates for {context.cve_details}') + + return {'docs': docs, 'query': result.query} + + async def rank(self, context: WindowsInternalsContext): + context.state_info.node.append(self.NODES.rank) + + chain = self.prompt_template.rank | LLM.o4_mini.with_structured_output(FileScoreList) + + files = '\n\n'.join(f"name: {doc.metadata.get('name', '')}\n{doc.page_content}" for doc, _ in context.docs) + metadata = json.dumps({k: v for k, v in context.cve_details.msrc_report.to_dict().items() if k != 'products'}) + + console.info(f'[*] Ranking {context.cve_details.cve} candidates') + + result: FileScoreList = await chain.ainvoke({ + 'query': context.query, + 'metadata': metadata, + 'files': files + }) + + score_map: dict[str, float] = {fs.file: fs.score for fs in result.files} + ranked_docs = [(doc, score, score_map.get(doc.metadata.get("name"), 0.0)) for doc, score in context.docs] + + return {'candidates': Candidates(query=context.query, + results=sorted(ranked_docs, key=lambda t: t[2], + reverse=True))} + diff --git a/supervisor_chat_assistant/__init__.py b/supervisor_chat_assistant/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/supervisor_chat_assistant/chat.py b/supervisor_chat_assistant/chat.py new file mode 100644 index 0000000..e69de29 diff --git a/vr_agent/__init__.py b/vr_agent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vr_agent/prompts.py b/vr_agent/prompts.py new file mode 100644 index 0000000..1b05464 --- /dev/null +++ b/vr_agent/prompts.py @@ -0,0 +1,115 @@ +SYSTEM_SCORE_FUNCTIONS = """ +You are a senior Windows-kernel vulnerability analyst and reverse-engineering specialist. +Your job: review *each* function diff and decide how likely the change is part of a **security +patch**. Identify the probable vulnerability class whenever a fix is suspected. + +Heuristics (non-exhaustive): +• Added/modified `if (Feature_…())` or other feature-flag guards +• New length/size checks → buffer/heap/stack overflow mitigation +• Added pointer/null checks → use-after-free / NULL-deref +• Added range, type, or privilege checks → priv-escalation / info-leak +• Memory clearing or allocation-size change → uninitialized-memory fix +• Cryptographic or auth logic change → crypto/auth bypass + +Only use evidence in the diff + metadata; **do not hallucinate**. +""" + +SYSTEM_GENERATE_REPORT_old = """ +You are an expert Windows vulnerability researcher and expert reverse engineering. +Your task is to get code samples that probably contains a security patch and identify +the vulnerability it's try to fix. + +Tip: Usually it involve a feature flag function in if condition [e.g. Feature_...()] + +Write a plain text report without unicode chars, that +writeup in details the root-cause-analysis, including code snippets. Explain exactly what +the vulnerability is, the attack vector, top-down review of triggering it. Be consistent +and ONLY use facts from the provided data. + +Start the report with this title +-------------------------------------------------------------------- +<CVE> Report +-------------------------------------------------------------------- + +The report **MUST** include (but not limited to) these sections: +Component +Vulnerability Class +Detailed root cause analysis +Vulnerability code snippets +Trigger flow (top-down) +Patch description +Attack Vector +Security Impact +Fix Effectiveness (if there is a vulnerability in the fix, analyze it as well) + +section header: +section title +-------------------------------------------------------------------- + +**MUST** - Wrap the report text so every line will be approximately 70 chars. + +If you are not confidence enough, you can return found = False +""" + + +SYSTEM_GENERATE_REPORT = """ +You are a senior Windows-kernel vulnerability analyst and reverse- +engineering specialist. + +Goal → Produce a **plain-ASCII** RCA report for the patched issue described +by the supplied diffs + metadata. Use *only* the evidence given; if a fact +is not present, write “unknown”. + +──────────────────────────────────────────────────────────────────────── +TITLE BLOCK – MANDATORY +──────────────────────────────────────────────────────────────────────── +Exactly two dashed lines and a CVE placeholder: + +-------------------------------------------------------------------- +<CVE> Report +-------------------------------------------------------------------- + +Replace <CVE> with the real ID if provided, otherwise “Unknown-CVE”. + +──────────────────────────────────────────────────────────────────────── +SECTION LAYOUT – MANDATORY (KEEP ORDER) +──────────────────────────────────────────────────────────────────────── +For *every* section below: +1. Print the section title on its own line +2. Immediately follow with + -------------------------------------------------------------------- +3. Write wrapped text (≈70 chars per line, no Unicode) +4. Leave one blank line before the next section header + +Required sections **in this exact order**: + +Component +Vulnerability Class +Detailed Root Cause Analysis +Vulnerability Code Snippets +Trigger Flow (Top-Down) +Attack Vector +Patch Description +Security Impact +Fix Effectiveness + + +Section "Detailed Root Cause Analysis" is the most important one. Write +all the details about the vulnerability's root cause, and include the parameters +and structures that were affected. + +──────────────────────────────────────────────────────────────────────── +FORMATTING RULES +──────────────────────────────────────────────────────────────────────── +• ASCII only – no “smart quotes”, em-dashes, or other Unicode glyphs. +• Hard-wrap to ≤70 chars per line; never exceed 72. +• Use fenced code blocks *only* inside “Vulnerability Code Snippets”. + ```c + // up to ~30 lines, copied or minimally adapted from diff + ``` + +• Keep each section concise but technically precise. +• Do **not** hallucinate; cite only observable behavior from the diff. + +""" +# • If overall confidence <0.5, output exactly: diff --git a/vr_agent/vulnerability_researcher_agent.py b/vr_agent/vulnerability_researcher_agent.py new file mode 100644 index 0000000..b6b4e75 --- /dev/null +++ b/vr_agent/vulnerability_researcher_agent.py @@ -0,0 +1,524 @@ +import asyncio +import datetime +import difflib +import json +import time +import uuid + +from dataclasses import dataclass +from pathlib import Path + +from binexport import FunctionBinExport +from langchain_core.documents import Document +from langchain_core.messages import SystemMessage, HumanMessage +from langchain_core.messages.utils import count_tokens_approximately +from langchain_core.prompt_values import PromptValue +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langchain_core.runnables import RunnableConfig +from langgraph.graph import StateGraph +from pydantic import BaseModel, Field + +from agent import Agent +from agent_tools.vector_store import VectorStore +from common import StateInfo, Artifact, logger, Timer, Report, CveDetails, LLM, Threshold, console +from defaultdataclass import defaultdataclass, field +from vr_agent.prompts import SYSTEM_SCORE_FUNCTIONS, SYSTEM_GENERATE_REPORT + + +@defaultdataclass +class DecompiledFunctionMetadata: + file: str + name: str + address: str + parents: str + + +@defaultdataclass +class DecompiledFunction: + before: str + after: str + udiff: str + metadata: DecompiledFunctionMetadata + score: float + + +@defaultdataclass +class VulnerabilityResearchContext: + state_info: StateInfo + artifact: Artifact + cve_details: CveDetails + decompiled: list[DecompiledFunction] + reports: list[Report] = field(default_factory=list) + + +@defaultdataclass +class VulnerabilityResearchOutput: + reports: list[Report] # = field(default_factory=list) + + +@defaultdataclass +class PromptTemplate: + analyze: ChatPromptTemplate + rank: ChatPromptTemplate + + +class Metadata(BaseModel): + file: str = Field( + ..., + description="Filename where the function resides (e.g., 'kernel32.dll')" + ) + name: str = Field( + ..., + description="Function name (e.g., 'NtCreateFile')" + ) + address: str = Field( + ..., + description="Function address or RVA (e.g., '0x7FFFB12A')" + ) + + +class FunctionRelevancy(BaseModel): + metadata: Metadata = Field( + ..., + description="Dictionary containing exactly the keys 'file', 'name', and 'address'" + ) + score: float = Field( + ..., + description="Scoring rubric (hard boundaries):\n" + "0.00 … Clearly cosmetic / refactor\n" + "0.25 … Unlikely security, minor logic tweak\n" + "0.50 … Possibly related (some safety hints)\n" + "0.75 … Probable security fix (strong indicators)\n" + "1.00 … Direct, obvious patch for a vulnerability" + ) + why: str = Field( + ..., + description="Explain why you score this function like this" + ) + + +class VulnFuncs(BaseModel): + functions: list[FunctionRelevancy] = Field( + ..., + description="List of FunctionRelevancy objects." + ) + + class Config: + json_schema_extra = { + "example": [ + { + "metadata": { + "file": "kernel32.dll", + "name": "NtCreateFile", + "address": "7FFFB12A" + }, + "score": 0.78 + }, + { + "metadata": { + "file": "ntoskrnl.exe", + "name": "ExAllocatePoolWithTag", + "address": "FFFFF800" + }, + "score": 0.45 + } + ] + } + + +class VulnReport(BaseModel): + found: bool = Field(..., description="If the vulnerability was found successfully") + confidence: float = Field(..., + description="Confidence of the right vulnerability was found - score from 0.0 to 1.0 " + "higher is better") + report: str = Field(..., description="The report") + + +def discover_parents(func: FunctionBinExport, iteration=0): + if func: + if not func.parents or iteration > 9: + yield [func.name] + else: + for parent in func.parents: + for prefix in discover_parents(parent, iteration + 1): + yield prefix + [func.name] + else: + yield None + + +async def add_to_store(artifact: Artifact): + with Timer(f'indexing {artifact.primary_file.name} to vector store'): + primary_path = Path(artifact.primary_file.path).parent / '__funcs__' + secondary_path = Path(artifact.secondary_file.path).parent / '__funcs__' + logger.info(f'{artifact.primary_file.name} has {len(artifact.changed)} functions modified ' + f'in {artifact.primary_file.kb} update') + + new_artifacts: list[Document] = [] + primary_metadata = artifact.primary_file.to_dict() + primary_metadata['version'] = '.'.join(str(x) for x in primary_metadata['version']) + + secondary_metadata = artifact.secondary_file.to_dict() + secondary_metadata['version'] = '.'.join(str(x) for x in secondary_metadata['version']) + + for func in artifact.changed: + before_code = None + after_code = None + + res = VectorStore.func_logic.get( + where={'$and': [{'uid': artifact.secondary_file.uid}, + {'address': f'{func.address2:X}'}]}, + ) + if not res.get('ids'): + try: + # TODO: maybe fallback to base + before_code = (secondary_path / f'{func.address2:X}.c').read_text(encoding="utf-8") + except FileNotFoundError: + continue + + res = VectorStore.func_logic.get( + where={'$and': [{'uid': artifact.primary_file.uid}, + {'address': f'{func.address1:X}'}]}, + ) + if not res.get('ids'): + try: + after_code = (primary_path / f'{func.address1:X}.c').read_text(encoding="utf-8") + except FileNotFoundError: + continue + + if before_code and after_code: + new_artifacts.append(Document(page_content=after_code, + metadata={**primary_metadata, + 'address': f'{func.address1:X}', + 'parents': json.dumps(next(discover_parents( + artifact.diff.primary.get(func.address1)), None))})) + new_artifacts.append(Document(page_content=before_code, + metadata={**secondary_metadata, + 'address': f'{func.address2:X}', + 'parents': json.dumps(next(discover_parents( + artifact.diff.secondary.get(func.address2)), None))})) + + if new_artifacts: + try: + await VectorStore.func_logic.aadd_documents(documents=new_artifacts, + ids=[str(uuid.uuid4()) for i in range(len(new_artifacts))]) + + logger.info(f'{len(new_artifacts)} functions from {artifact.primary_file.name} ' + f'were embedded to vector store') + except BaseException as e: + logger.error(f'Failed to add functions to vector store: {e}') + + +class VulnerabilityResearch(Agent): + ''' + Agent that can analyze vulnerabilities within snippet code with context + 1. Get list of code snippet pairs (before and after) and rank relevancy by its probability to be a security change + 2. Get list of code snippet pairs (before and after) and find vulnerability according to context and generate report + 3. Human interaction using chat + 4. Windbg MCP (* Future) + ''' + + prompt_template: PromptTemplate = PromptTemplate( + rank=ChatPromptTemplate([ + SystemMessage(SYSTEM_SCORE_FUNCTIONS), + MessagesPlaceholder('functions'), + MessagesPlaceholder('metadata') + ]), + analyze=ChatPromptTemplate([ + SystemMessage(SYSTEM_GENERATE_REPORT), + MessagesPlaceholder('functions'), + MessagesPlaceholder('metadata') + ]) + ) + + @dataclass(frozen=True) + class NODES: + indexing = "Indexing artifacts" + rank = "Rank artifacts" + analyze = "Root cause analysis" + generate = "Generate report" + + def __init__(self): + super().__init__() + self._iter = 3 + + def _build(self): + if self._graph: + return + + builder = StateGraph(state_schema=VulnerabilityResearchContext, output=VulnerabilityResearchOutput) + + builder.add_node(self.NODES.analyze, self.analyze) + builder.add_node(self.NODES.rank, self.rank) + builder.add_node(self.NODES.generate, self.generate) + builder.add_node(self.NODES.indexing, self.indexing) + + builder.set_entry_point(self.NODES.indexing) + builder.add_edge(self.NODES.indexing, self.NODES.rank) + builder.add_edge(self.NODES.rank, self.NODES.analyze) + builder.add_conditional_edges(self.NODES.analyze, self.refinement, [ + self.NODES.rank, + self.NODES.generate + ]) + builder.set_finish_point(self.NODES.generate) + + self._graph = builder.compile() + + def refinement(self, context: VulnerabilityResearchContext, config: RunnableConfig): + self._iter -= 1 + th = config.get('configurable', {}).get('threshold', Threshold()) + if context.reports and context.reports[0].confidence > th.report: + console.info(f'[+] Found {context.cve_details.cve} security flaw ' + f'with confidence: {context.reports[0].confidence}') + return self.NODES.generate + else: + # Try again + if self._iter > 0 and context.decompiled: + console.info(f'[-] Cannot find {context.cve_details.cve} distinct security flaw, try again') + return self.NODES.rank + else: + console.info(f'[-] Failed to find {context.cve_details.cve} security flaw within the iteration limit') + return self.NODES.generate + + async def indexing(self, context: VulnerabilityResearchContext): + context.state_info.node.append(self.NODES.indexing) + # await add_to_store(context.artifact) + + primary_path = Path(context.artifact.primary_file.path).parent / '__funcs__' + secondary_path = Path(context.artifact.secondary_file.path).parent / '__funcs__' + logger.info(f'{context.artifact.primary_file.name} has {len(context.artifact.changed)} functions modified ' + f'in {context.artifact.primary_file.kb} update') + + functions: list[DecompiledFunction] = [] + for func in context.artifact.changed: + before_code = None + after_code = None + + try: + # TODO: maybe fallback to base + before_code = (secondary_path / f'{func.address2:X}.c').read_text(encoding="utf-8") + except FileNotFoundError: + continue + + try: + after_code = (primary_path / f'{func.address1:X}.c').read_text(encoding="utf-8") + except FileNotFoundError: + continue + + # before = VectorStore.func_logic.get( + # where={'$and': [{'uid': context.artifact.secondary_file.uid}, + # {'address': f'{func.address2:X}'}]}, + # ) + # + # after = VectorStore.func_logic.get( + # where={'$and': [{'uid': context.artifact.primary_file.uid}, + # {'address': f'{func.address1:X}'}]}, + # ) + + try: + udiff = difflib.unified_diff( + # before['documents'][0].splitlines(), + # after['documents'][0].splitlines(), + before_code.splitlines(), + after_code.splitlines(), + fromfile=f'{func.name2} before', + tofile=f'{func.name1} after', + lineterm="" + ) + except BaseException as e: + continue + + functions.append(DecompiledFunction( + before=before_code, + after=after_code, + udiff='\n'.join(udiff), + metadata=DecompiledFunctionMetadata( + file=context.artifact.primary_file.name, + name=func.name1, + address=f'{func.address1:X}', + # parents=after['metadatas'][0].get('parents'), + parents=json.dumps(next(discover_parents( + context.artifact.diff.secondary.get(func.address1)), None)) + ))) + + return { + 'decompiled': functions, + } + + async def rank(self, context: VulnerabilityResearchContext): + context.state_info.node.append(self.NODES.rank) + + if not context.decompiled: + logger.error('No decompiled functions') + return {} + + changes = [HumanMessage(f'\n\n------------------------------------\n' + f'function metadata: {func.metadata}\n' + f'changes:\n{func.udiff}\n\n' + f'------------------------------------\n\n') + for func in context.decompiled if func.score is None] + + metadata = [HumanMessage(f'Metadata about the vulnerability: [' + f'{context.cve_details.cve}' + f'{context.cve_details.msrc_report.title}' + f'{context.cve_details.msrc_report.description}' + f'{context.cve_details.msrc_report.faq}' + f'{context.cve_details.msrc_report.cwe}]')] + + prompt: PromptValue | None = None + n = 0 + for i in range(len(changes), 0, -1): + prompt = self.prompt_template.rank.invoke({ + 'functions': changes[:i], + 'metadata': metadata, + }) + + if count_tokens_approximately(prompt) < 100_000: + n = i + break + + if not prompt: + return {} + + console.info(f'[*] Ranking {n} modified artifacts out of {len(context.decompiled)}') + llm = LLM.o4_mini + result = await llm.with_structured_output(VulnFuncs, include_raw=True).ainvoke(prompt) + + if result["raw"].usage_metadata: + logger.info(f'Usage: {result["raw"].usage_metadata.get("total_tokens")} tokens using {llm.model_name}') + + if result['parsing_error']: + logger.error(f'parsing error: {result["parsing_error"]}') + return {} + + scored_functions: list[FunctionRelevancy] = result['parsed'].functions + logger.debug('\n'.join(str(x) for x in scored_functions)) # TODO: change to debug + + for scored in scored_functions: + for func in context.decompiled: + if scored.metadata.address == func.metadata.address: + func.score = scored.score + break + + return { + 'decompiled': context.decompiled + } + + async def analyze(self, context: VulnerabilityResearchContext, config: RunnableConfig): + context.state_info.node.append(self.NODES.analyze) + + if not context.decompiled: + logger.error('No decompiled functions') + return {} + + th = config.get('configurable', {}).get('threshold', Threshold()) + functions: list[DecompiledFunction] = [] + for func in context.decompiled: + if not func.score or func.score < th.security_modification: + continue + functions.append(func) + + if len(functions): + console.info(f'[+] Found {len(functions)} possible security modification ' + f'in {context.artifact.primary_file.name}') + else: + console.info(f'[-] Failed to find security modification ' + f'in {context.artifact.primary_file.name}') + + changes = [HumanMessage(f'\n\n------------------------------------\n' + f'function metadata: {artifact.metadata}\n' + f'before patch:\n{artifact.before}\n\n' + f'patch:\n{artifact.udiff}\n\n' + f'------------------------------------\n\n') + for artifact in functions] + + metadata = [HumanMessage(f'Metadata about the vulnerability: [' + f'{context.cve_details.cve}' + f'{context.cve_details.msrc_report.title}' + f'{context.cve_details.msrc_report.description}' + f'{context.cve_details.msrc_report.faq}' + f'{context.cve_details.msrc_report.cwe}]')] + + prompt: PromptValue | None = None + count = 0 + for i in range(len(changes), 0, -1): + prompt = self.prompt_template.analyze.invoke({ + 'functions': changes[:i], + 'metadata': metadata + }) + if count_tokens_approximately(prompt) < 100_000: + count = i + break + + if not prompt: + return {} + + console.info(f'[*] Analyze {context.artifact.primary_file.name} using {count} artifacts') + llm = LLM.o3 + result = await llm.with_structured_output(VulnReport, include_raw=True).ainvoke(prompt) + + if result["raw"].usage_metadata: + logger.info(f'Usage: {result["raw"].usage_metadata.get("total_tokens")} tokens using {llm.model_name}') + + if result['parsing_error']: + logger.error(f'parsing error: {result["parsing_error"]}') + return {} + + analysis_report: VulnReport = result['parsed'] + + remainder = [f for f in context.decompiled if f not in functions] + + if analysis_report.found: + reports = [Report(cve_details=context.cve_details, + artifact=context.artifact, + content=analysis_report.report, + confidence=analysis_report.confidence)] + reports.extend(context.reports or []) + + return { + 'reports': reports, + 'decompiled': remainder, + } + + return { + 'decompiled': remainder, + } + + async def generate(self, context: VulnerabilityResearchContext): + context.state_info.node.append(self.NODES.generate) + + if context.reports: + docs: list[Document] = [] + + for report in context.reports: + console.info(f'[+] Found {report.cve_details.cve} security flaw in {report.artifact.primary_file.name}') + # res = VectorStore.reports.get( + # where={'$and': [{'cve': report.cve_details.cve}, + # {'file': report.artifact.primary_file.name}]}, + # ) + # + # if not res.get('ids'): + docs.append(Document( + page_content=report.content, + metadata={ + 'cve': report.cve_details.cve, + 'kb': report.artifact.primary_file.kb, + 'file': report.artifact.primary_file.name, + 'patch_store_uid': report.artifact.primary_file.uid, + 'confidence': report.confidence, + 'change_count': len(report.artifact.changed), + 'date': time.time() + + } + )) + + if docs: + try: + await VectorStore.reports.aadd_documents(documents=docs, + ids=[str(uuid.uuid4()) for i in + range(len(docs))]) + console.info(f'[+] {context.cve_details.cve}: {len(docs)} reports generated ' + f'and cached successfully') + except BaseException as e: + logger.error(f'Failed add to reports vector store: {e}') + + return {'reports': context.reports or []}