Security Vulnerability – DLL Hijacking During ClickOnce Application Startup (dfshim.dll)

Nagarajan Dhanapal 5 Reputation points
2026-10-18T08:27:01.52+00:00

Issue Description

We have identified a potential DLL hijacking vulnerability in our .NET Framework 4.8 application deployed using Visual Studio ClickOnce publishing (.application manifest).

During security testing, we observed that placing a DLL named dfshim.dll in the ClickOnce application's local execution directory under the user's AppData folder may result in the DLL being executed before our application's Program.Main() method is reached.

As a proof of concept, we placed a test DLL named dfshim.dll in the ClickOnce application directory. When launching the application through its ClickOnce shortcut (.appref-ms), the test DLL created a file on the Desktop before the application's Program.Main() MessageBox appeared.

This raises concerns about potential code execution during application startup, before application-level security checks or DLL signature validation can be performed.

Environment

Application: .NET Framework 4.8

Deployment: Visual Studio ClickOnce

Manifest: .application

Launch method: ClickOnce application shortcut (.appref-ms)

Test environment: Windows

Investigation Findings

Using Process Monitor, we observed the following:

The process attempted to locate dfshim.dll in the ClickOnce application directory under the user's AppData folder. The lookup returned NAME NOT FOUND.

The process subsequently loaded dfshim.dll successfully from C:\Windows\SysWOW64\dfshim.dll.

In our proof-of-concept test, the test DLL placed in the ClickOnce application directory created a file on the Desktop before the application's Program.Main() MessageBox appeared.

We do not explicitly reference or deploy dfshim.dll as part of our application. It is associated with the ClickOnce deployment and startup mechanism.

Other DLLs for Investigation

We are also concerned that similar DLL hijacking issues may exist with the following DLLs identified during our security assessment:

dfshim.dll

urlmon.dll

iertutil.dll

netutils.dll

dwrite.dll

WindowsCodecs.dll

wbemcomn.dll

uxtheme.dll

comctl32.dll

cryptnet.dll

shell32.dll

Expected Outcome

We require a permanent, Microsoft-supported remediation to prevent untrusted DLLs from executing during application startup, before application-level security controls are initialized, without compromising the functionality or deployment of our existing .NET Framework 4.8 ClickOnce application.

Please review the findings and advise on the appropriate remediation approach.

Thank you.

Developer technologies | .NET | .NET Runtime
0 comments No comments

2 answers

Sort by: Newest
  1. Brian Pham (WICLOUD CORPORATION) 735 Reputation points Microsoft External Staff Moderator
    2026-10-19T07:40:08.0133333+00:00

    Hi @Nagarajan Dhanapal ,

    Thank you for providing the detailed investigation and proof-of-concept results. I understand the concern that the test dfshim.dll appears to execute during ClickOnce activation before your application reaches Program.Main().

    Because the reported behavior potentially involves a Microsoft ClickOnce or Windows activation component and requires a formal security-boundary assessment, we recommend submitting the finding privately through the Microsoft Security Response Center.

    Please include the following in the submission:

    1. A minimal ClickOnce application that reproduces the behavior.
    2. The harmless proof-of-concept DLL and its source code, if available.
    3. Exact reproduction steps using the .appref-ms shortcut.
    4. The Process Monitor .PML trace showing the successful Load Image event.
    5. The loading process name, executable path, parent process, command line, PID, and architecture.
    6. The Windows edition, OS build, architecture, and installed updates.
    7. The installed .NET Framework 4.8 release value.
    8. The ClickOnce deployment and application manifests.
    9. The affected directory permissions and the identity used to place the DLL.
    10. Whether the DLL executes under the same user context or crosses a privilege or integrity boundary.

    Please share the proof of concept and sensitive diagnostic data only through MSRC. Each reported DLL should have evidence of successful loading and code execution, as a NAME NOT FOUND event alone does not confirm exploitability.

    Until MSRC completes its assessment, avoid changing the ClickOnce cache permissions or treating MSI migration as a confirmed fix. Prefer system32 images may be tested as a temporary mitigation on the identified loading process, with appropriate compatibility testing.

    If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide for your confirmation. Thank you.

    Was this answer helpful?


  2. AI answer

    2026-10-18T08:28:45.8833333+00:00

    Because the DLL executes before Program.Main(), the only supported in-place mitigation to test is Windows Exploit Protection’s PreferSystem32 policy on the executable that performs ClickOnce activation; application code cannot retroactively secure that startup load.

    Test the in-place mitigation

    Microsoft introduced PreferSystem32 specifically for application-directory planting of system DLL names: it searches System32 before the application directory. Microsoft classifies application-directory planting as defense in depth rather than generally servicing it with a down-level security update.^1^

    1. Identify the process that loads the planted DLL.
      • Capture another .appref-ms launch in Process Monitor.
      • Filter for:
        • Operation is Load Image
        • Path ends with dfshim.dll
      • Record the Process Name and full executable path on the successful malicious load.
      • Do not assume that your application executable is the correct policy target; the load occurs before its entry point.
    2. Apply PreferSystem32 to that executable in a test system.
      • Open Windows Security → App & browser control → Exploit protection → Program settings.
      • Select Add program to customize.
      • Prefer Choose exact file path and select the executable identified above.
      • Locate Prefer system32 images/PreferSystem32, override the system setting, and enable it.
      • Apply the change and restart the affected process or Windows when prompted. Windows supports per-program Exploit Protection configuration through this interface.^2^
    3. Retest the proof of concept.
      • Leave the test dfshim.dll in the ClickOnce application directory.
      • Launch through the same .appref-ms shortcut.
      • Expected result: the applicable system DLL is loaded from the Windows system directory instead of the application directory, and the proof-of-concept file is not created.
      • Confirm the successful Load Image path in Process Monitor.
    4. Verify the stored policy from elevated PowerShell:
    $process = "actual-loader.exe"
    Get-ProcessMitigation -Name $process
    
    # Start the ClickOnce application, then inspect the running instance.
    Get-ProcessMitigation -Name $process -RunningProcesses
    

    Get-ProcessMitigation and Set-ProcessMitigation require an elevated PowerShell session.^3^

    PreferSystem32 is a compensating control, not a general prohibition on loading DLLs from the application directory. It is relevant to system DLL names such as those in your list, must be applied to the process performing the vulnerable load, and requires compatibility testing. It does not establish that every listed name is exploitable.

    Why Program.Main() cannot fix this case

    For unpackaged desktop applications, the normal DLL search process considers redirection, API sets, already loaded modules, Known DLLs, and then the application directory before the system directory. The precise result therefore varies by DLL, Windows version, architecture, process, and loading method.^4^

    Calling SetDefaultDllDirectories or SetDllDirectory("") at the beginning of Main() can harden subsequent loads performed by your process, but cannot affect dfshim.dll or another component already loaded during .appref-ms activation. For DLLs loaded later by code you control:

    • Use fully qualified paths.
    • Prefer LoadLibraryEx search flags or SetDefaultDllDirectories.
    • Test process-wide changes against plug-ins and native dependencies.^5^

    Stronger permanent remediation

    If PreferSystem32 cannot be applied to the actual activation process, fails testing, or does not cover all observed loads, retaining the same per-user ClickOnce execution model does not provide the requested guarantee.

    ClickOnce deliberately stores and executes application files from a per-user, versioned cache, and manages replacement directories during updates.^6^ Avoid changing ACLs inside that cache: ClickOnce manages those directories, making such changes operationally fragile across updates.

    The stronger remediation is:

    1. Deploy the executable and native dependencies with MSI or another managed installer.
    2. Install code under an administrator-protected location writable only by administrators, such as Program Files.
    3. Store user-writable settings and data outside the executable directory.
    4. Sign the installer and application binaries.
    5. Retire the .appref-ms shortcut and launch the installed executable.
    6. As a standard user, verify that creating or replacing DLLs in the installation directory is denied.
    7. Repeat the Process Monitor test and confirm system DLLs load from the expected Windows directory.

    ClickOnce is intentionally per-user and does not install files under Program Files, whereas Windows Installer commonly requires administrative installation.^7^

    Signing the ClickOnce manifests remains recommended, but it is not a substitute for controlling the DLL search path. Manifest signing proves publisher identity and protects manifested content against tampering.^8^ ClickOnce hashes likewise validate files declared in the application manifest; an omitted, externally planted DLL is a different loading-path issue.^9^

    Validate each additional DLL separately

    Do not classify all listed DLLs from NAME NOT FOUND events alone. For each name, record:

    • Process performing the lookup.
    • Load Image success path.
    • Whether the planted DLL actually loads and executes.
    • Process architecture—particularly System32 versus SysWOW64.
    • Whether PreferSystem32 changes the successful load path.

    If a Microsoft-supplied activation process still loads a planted system DLL after PreferSystem32 is correctly applied, submit the Process Monitor trace, Windows build, architecture, exact reproduction steps, and harmless proof of concept through the Microsoft Security Response Center submission portal.


    References

    1. Triaging a DLL planting vulnerability - microsoft.com
    2. Evaluate exploit protection
    3. Enable exploit protection
    4. Dynamic-link library search order
    5. Dynamic-Link Library Security
    6. ClickOnce cache overview
    7. ClickOnce Deployment and Security - Visual Studio (Windows)
    8. Secure ClickOnce applications
    9. <file> element (ClickOnce application)
    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

    1 person found this answer helpful.

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.