You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止.NET Framework从当前工作目录加载程序集?

解决.NET Framework下CWE-427系统DLL劫持问题

你的场景核心是:.NET Framework程序因Windows默认DLL搜索顺序优先检查EXE所在目录,导致恶意伪造的系统DLL(如propsys.dll)被加载,触发CWE-427漏洞。以下是针对无外部依赖、需兼容低版本.NET Framework的解决方案:

一、强制锁定系统DLL加载路径

Windows的LoadLibrary默认会先搜索EXE所在目录,这是劫持的根源。你可以通过P/Invoke调用SetDllDirectory API,清空额外搜索路径,让系统仅从System32/Windows目录加载DLL:

using System.Runtime.InteropServices;

class Program
{
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SetDllDirectory(string lpPathName);

    static void Main()
    {
        // 清空自定义DLL搜索目录,仅保留系统默认路径
        SetDllDirectory("");
        
        // 启动程序主逻辑
        // ...
    }
}

这个操作要放在程序启动的最早期(Main方法第一行),确保在任何系统DLL加载前生效。由于你的程序无外部依赖,不会影响合法DLL的加载。

二、验证系统DLL的官方数字签名

通过检查加载的DLL是否带有微软官方有效签名,彻底阻断恶意DLL的执行。核心是调用Windows的WinVerifyTrust API实现签名验证:

using System;
using System.Runtime.InteropServices;

class DllSignatureValidator
{
    private const uint WINTRUST_ACTION_GENERIC_VERIFY_V2 = 0x00000002;
    private const uint ERROR_SUCCESS = 0;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct WINTRUST_FILE_INFO
    {
        public uint cbStruct;
        public string pcwszFilePath;
        public IntPtr hFile;
        public IntPtr pgKnownSubject;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct WINTRUST_DATA
    {
        public uint cbStruct;
        public IntPtr pPolicyCallbackData;
        public IntPtr pSIPClientData;
        public uint dwUIChoice;
        public uint fdwRevocationChecks;
        public uint dwUnionChoice;
        public IntPtr pFile;
        public uint dwStateAction;
        public IntPtr hWVTStateData;
        public string pwszURLReference;
        public uint dwProvFlags;
        public uint dwUIContext;
        public IntPtr pSignatureSettings;
    }

    [DllImport("wintrust.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern uint WinVerifyTrust(IntPtr hwnd, ref Guid pgActionID, ref WINTRUST_DATA pWVTData);

    public static bool IsMicrosoftSignedDll(string dllPath)
    {
        var actionGuid = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}");
        var fileInfo = new WINTRUST_FILE_INFO
        {
            cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)),
            pcwszFilePath = dllPath
        };

        var trustData = new WINTRUST_DATA
        {
            cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA)),
            dwUIChoice = 2, // 不显示UI
            fdwRevocationChecks = 0,
            dwUnionChoice = 1, // 验证文件
            pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_FILE_INFO))),
            dwStateAction = 0, // 执行验证
            dwProvFlags = 0x00000010 // 跳过吊销检查(可按需调整)
        };

        Marshal.StructureToPtr(fileInfo, trustData.pFile, false);
        uint result = WinVerifyTrust(IntPtr.Zero, ref actionGuid, ref trustData);
        Marshal.FreeHGlobal(trustData.pFile);

        // 完整实现需额外检查证书链是否归属微软,此处简化为基础签名验证
        return result == ERROR_SUCCESS;
    }
}

使用时,在程序启动前预加载并验证关键系统DLL:

using System.IO;

static void Main()
{
    string propsysPath = Path.Combine(Environment.SystemDirectory, "propsys.dll");
    if (!DllSignatureValidator.IsMicrosoftSignedDll(propsysPath))
    {
        // 验证失败,终止程序
        Environment.Exit(1);
    }

    // 后续程序逻辑
}

三、补充说明

  • 以上方案均兼容低版本.NET Framework(如4.0、3.5),无需升级框架版本。
  • 若攻击者已能修改EXE所在目录,虽然签名能保障EXE可信,但仍需通过上述方法阻断DLL劫持路径。
  • .NET 8通过修改默认DLL搜索顺序解决了该问题,但低版本框架需手动干预。

内容的提问来源于stack exchange,提问作者Bryan Dam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:52:37