[Windows Defender] On the Trust-Verification Gap in the Windows Defender Platform Update Path

SeongJin. H·2026년 9월 30일

Security

목록 보기
2/4

This paper examines a structural trust-verification weakness discovered through static analysis of a legitimately signed Windows Defender executable, MsMpEng.exe, file version 4.18.25080.5.

The binary under study carries a valid Microsoft Authenticode signature and shows no evidence of packing or obfuscation, which made it possible to trace its control flow through disassembly and decompilation with reasonable confidence.

The analysis focused on the small cluster of functions responsible for locating, verifying, and loading a replacement engine component, mpsvc.dll, from a so-called drop folder: MpCheckPlatformUpdate, MpCheckLatestDropLocation, ValidateDropFolderPayload, and LoadMpSvcFrom. Two structural observations emerged from tracing these functions.

First, the step that verifies a file and the step that subsequently loads it are carried out as two separate filesystem accesses at two different points in time, rather than through a single, continuously held handle.

Second, the strength of the verification that is actually applied is not fixed in code but is instead governed at runtime by a single value read from the registry.

The paper does not claim that these observations amount to a confirmed, exploitable vulnerability; rather, it sets out to document, in a disciplined way, the logical risk factors that static analysis alone can establish, and to identify the specific points that would need further, dynamic verification before any stronger claim could be made.

1. Introduction

Antivirus software occupies one of the few software categories that is permitted to intervene, with system-level privilege, across a machine's filesystem, process space, and registry. Because of this privileged position, the self-defense and self-update logic of antivirus products has long attracted the attention of security researchers in its own right.

The update pipeline in particular, which periodically fetches a new engine or signature database, verifies it, and then loads it, is structurally exposed to time-of-check-to-time-of-use problems, since by its nature it must repeatedly cross a trust boundary on the filesystem. Windows Defender is no exception to this pattern: prior public reports have described issues in its update-related handling of log files through directory substitution, as well as insufficient integrity verification of its signature database files, both of which were subsequently corrected.

This paper summarizes the results of a purely static analysis of a single MsMpEng.exe binary supplied by the user. The analysis began by verifying the file's Authenticode signature to establish, before anything else, that the sample was in fact a genuine Microsoft-signed release rather than a forged or repackaged file.

From there, the surviving symbol information in the binary was used to identify the functions directly responsible for the update logic, and their decompiled pseudocode was traced in sequence to determine precisely where, and by what mechanism, trust verification actually takes place.

2. Subject and Methodology

The file under analysis is a 282,480-byte PE32+ executable. Its version resource records file version 4.18.25080.5, a product name of the Windows operating system, and a file description of Antimalware Service Executable. Offline verification of the embedded Authenticode signature, performed by comparing the digest recorded in the signature against a digest computed directly from the file, showed an exact match. This strongly indicates that the file has not been modified since it was signed, and it was on this basis that the sample was treated as a genuine Microsoft release rather than a counterfeit one.

Full certificate-chain validation could not be completed in the analysis environment, since the local trust store lacked the relevant root certificate; this is a limitation of the verification environment itself and has no bearing on the authenticity of the file.

The import table consists solely of CRYPT32, WINTRUST, ADVAPI32, KERNEL32, and ntdll, with no networking or remote-process-manipulation APIs present. This is consistent with the file being a comparatively small host process whose job is to locate, verify, and load a new engine component, rather than the detection engine itself, which resides in a separate, much larger module. The functions directly relevant to the update logic — MpCheckPlatformUpdate, MpCheckLatestDropLocation, ValidateDropFolderPayload, LoadMpSvcFrom, and AllowTestCert — were identified from the surviving symbol table, and their pseudocode was reconstructed and traced in call order to establish how they invoke one another and where their conditional branches diverge.

3. Structure of the Platform Update Mechanism

In outline, MpCheckPlatformUpdate first determines the execution environment and, in the ordinary case, calls MpCheckLatestDropLocation to determine the location of the drop folder in which a new engine component might be staged. This location is derived from a combination of a registry value and a version-formatted directory name on the filesystem, and the logic also compares the candidate location against a value previously recorded in the registry so that an update already applied is not reapplied. Where a new update is judged necessary, ValidateDropFolderPayload is invoked to verify the signatures of the key files inside that folder, and only if this verification succeeds does LoadMpSvcFrom go on to actually load mpsvc.dll.

There is nothing unusual about this structure in the abstract. The concern lies in two specific properties of the implementation: each stage accesses the filesystem separately and at a different point in time, and the strength of the verification that is applied at each stage is not fixed in the code but is instead determined, at runtime, by a value read from the registry.

4. LoadMpSvcFrom: The Separation of Verification from Use

The pseudocode of LoadMpSvcFrom first constructs the path to mpsvc.dll beneath the target directory as a string, then opens that path to obtain a file handle. Only if a specific bit is set in the flags value supplied by the caller is a Microsoft certificate-check function invoked against that handle; if the bit is not set, this check is skipped outright and execution proceeds to the next stage. As soon as the check completes, the handle is closed immediately, and the function then performs an entirely new open operation, using the same path string, in order to actually load the library.

The problem here is that nothing at the code level guarantees that the object that was verified and the object that is subsequently loaded into memory are one and the same.

Verification examines the contents of whatever file happened to be open at one point in time, while loading independently re-resolves the same path at a later point in time. If the file that a given path resolves to can change between these two moments, it becomes possible for verification to succeed against one file while loading proceeds against a different one. This class of defect is a well-known consequence of implementing verification and use as separate operations rather than operating continuously on a single handle, and it recurs wherever a path is re-resolved after the fact instead of the already-verified handle being carried forward.

v26 = AllowTestCert(a4);
v27 = ValidateDropFolderPayload(Str, v26);
if ( v27 >= 0 )
{
    v30 = 2 * AllowTestCert(a4) + 1;
    ...
    v27 = LoadMpSvcFrom(&hLibModule, v31, v30);

It is also worth noting that this verification step can be skipped entirely, depending on the flag value the caller supplies. Examining the calling site in MpCheckLatestDropLocation shows that, under the ordinary condition in which test-certificate acceptance is disabled, the flag is in fact passed in a form that does not request this check. As a result, on a typical system the final gate before a new engine is loaded is not the check inside LoadMpSvcFrom at all, but narrows down to the single call to ValidateDropFolderPayload that precedes it.

5. ValidateDropFolderPayload and the Limits of Per-File Signature Checking

ValidateDropFolderPayload performs an independent Microsoft certificate check against each of three files inside the drop folder: MpClient.dll, MpSvc.dll, and MsMpEng.exe. This check, too, operates on a path string rather than a handle, and because a non-trivial amount of additional work — registry updates, logging — takes place between the return of this function and the eventual invocation of LoadMpSvcFrom, the time window between verification and use is wider here than the one identified inside LoadMpSvcFrom itself.

It is also worth pointing out that these three files are not verified as a single, coherent unit, for instance through one signature covering a catalog of all of them together; each file is checked on its own. The fact that a given file carries a valid signature does not, by itself, guarantee that the three files together form a combination that Microsoft actually shipped as a unit. In principle, it would be possible to assemble files that were each genuinely, validly signed at different points in time into a combination that was never released as such, which would allow a mixture of an up-to-date component alongside an older component with a previously fixed issue to pass verification together.

6. The AllowTestCert Registry Flag and the Shifting Trust Boundary

The value that governs the strength of verification in both ValidateDropFolderPayload and LoadMpSvcFrom is the boolean returned by a function named AllowTestCert. Its pseudocode shows that this value has nothing to do with the operating system's own test-signing boot state; it is simply read directly from a single DWORD value under a specific registry key. When this value is true, an additional bit is set in the flags passed into the certificate-check function; whether that bit actually causes test or self-signed certificates to be accepted as valid can only be confirmed by examining the deeper internal logic of that check function. What can be confirmed from the code alone, however, is that the trust policy genuinely changes depending on whether this value is on or off.

The practical severity of this design ultimately comes down to who is able to write to that registry key. If the key is correctly restricted to administrative access, this is a reasonable design element intended for internal debugging or testing. If, on the other hand, the access control on that key or its parent path were to permit writes from a standard user, a local, non-administrative account would be able to lower the system's trust policy simply by changing a single registry value. Security-relevant policy stored in a registry location or file whose access control has not been carefully scrutinized, and which thereby becomes an attack target in its own right, is a pattern that has recurred repeatedly across a wide range of software.

7. A Composite Attack Scenario and Its Preconditions

Bringing these observations together suggests the following hypothesis. Assume a local attacker with non-administrative privileges who has write access sufficient to create a new subdirectory within the drop folder or its parent path. Such an attacker could first let a set of genuinely, validly signed components pass the check performed by ValidateDropFolderPayload, and then exploit the interval between the completion of that check and the moment LoadMpSvcFrom re-opens the file for loading in order to substitute different content at that same path. Should this succeed, it could result in attacker-chosen code executing inside a process running with system-level privilege.

Separately, if the registry key referenced by AllowTestCert turns out to be weakly protected, an attacker would not need to attempt a race condition at all: a simpler and more reliable path would be to flip that registry value to relax the trust policy directly, and then place a self-signed component in the drop folder without further complication.

Both scenarios, however, must be understood as hypotheses derived from static analysis rather than as established conclusions. Whether either actually holds can only be determined by directly inspecting the access-control lists on the drop folder and the relevant registry key on a real system, and by further analyzing the internal logic of MpUtilMicrosoftCertCheck and the folder-enumeration and version-comparison logic inside GetLatestDropFolderIdFrom.

Trust-verification problems in antivirus update pipelines are not a novel category of issue. In one previously reported case, Windows Defender's handling of log file rotation was found to allow a directory to be substituted with a mount point in a way that could be used to induce a system-privileged file operation; the issue was subsequently corrected.

Although this involves a different code path from the drop-folder logic examined in this paper, it shares the same underlying character, in that it targets a filesystem location that a system-privileged process accesses repeatedly, such as a log, configuration, or update path. In another case, independent research found that a signature database file was compressed but not encrypted, and was not sufficiently integrity-checked, which allowed a non-administrative user to tamper with the actual threat definitions it contained; this was later addressed by adding digital signature verification across the entire signature database file.

These prior cases share a common lesson: when an antivirus update pipeline consists of multiple stages of filesystem access and multiple components combined together, a single point of loose verification anywhere in that chain can weaken the trust model of the system as a whole. The separation of verification from use, and the shifting of trust policy through a registry flag, that were identified in this analysis can be understood in that same light.

It should also be noted that, around the time this analysis was conducted, a number of reports describing similarly named vulnerabilities in Defender's update and signature pipeline surfaced across different outlets, and that these reports were not always mutually consistent on basic facts such as whether a given issue had been patched or exactly which code path was involved. For that reason, this paper does not cite those recent reports as established fact, and mentions them only as evidence that scrutiny of the antivirus update logic in this space has continued into the present.

The direction of a fix for the issues identified here is reasonably clear. First, verification and use of a file should be carried out through the same file handle. Rather than closing the handle used for verification and re-opening the path afterward, the already-verified handle should be carried forward and used directly for loading, or, at minimum, the content behind that handle should be re-checked against the verified state immediately before loading takes place.

Second, an update composed of multiple files should be verified as a single unit, through one signature or catalog covering the combination as a whole, and that verification should include a check that the version being applied increases monotonically, so that rollback to an earlier version is prevented. Third, a value that directly governs the strength of a trust policy, such as the flag examined here, should be stored somewhere a standard user cannot reach, and ideally in a mechanism with stricter access enforcement than the registry.

Finally, paths such as the drop folder that a system-privileged process accesses repeatedly should have their access-control lists reviewed on a regular basis to confirm that write access has not been unintentionally granted to standard users.

10. Limitations

This paper rests entirely on static analysis, and specifically on pseudocode reconstructed by a decompiler, which imposes clear limits on what can be concluded. The actual access-control configuration of the drop folder and the relevant registry key on a real system was not examined, and the internal logic of functions that could directly affect the conclusions, such as MpUtilMicrosoftCertCheck and GetLatestDropFolderIdFrom, has not yet been analyzed.

Decompiled pseudocode can also diverge from the original source due to compiler optimization, and some of the variables and branch conditions encountered here appeared as the fused result of several such optimizations, which calls for caution in interpretation.

The attack scenarios presented in this paper should therefore be treated as hypotheses grounded in static analysis, not as confirmed vulnerabilities, until they have been checked against dynamic verification and the actual permission configuration of a real system.

11. Conclusion

Through static analysis of a genuinely signed Windows Defender executable, this paper has shown that the platform update logic separates file verification from file use into two distinct filesystem accesses occurring at two different points in time, and that the strength of that verification is governed by a single registry value read at runtime.

This structure does not, by itself, establish that a vulnerability is actually exploitable, but in an environment where access control is not sufficiently strict, it provides a logical foundation on which local privilege escalation could be built. Because the self-update logic of antivirus software underpins the trust model of the system as a whole, these structural points warrant continued scrutiny and improvement.

0개의 댓글