fix(inso): add SetDefaultDllDirectories, close cryptnet.dll sideload gap

Process Monitor confirmed cryptnet.dll (loaded internally by crypt32.dll's
own revocation-checking code, not by our own LoadLibrary calls) was
loaded from the local directory despite PreferSystem32Images being set —
a real, confirmed gap. SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32)
removes the application directory from the process-wide default DLL
search path entirely, covering loads issued by other DLLs internally,
not just our own explicit calls. Also switch our own wintrust.dll/
crypt32.dll LoadLibrary calls to LoadLibraryExW with the same flag as
an explicit, call-scoped belt-and-suspenders on top of the process-wide
setting.
This commit is contained in:
Kyle
2026-07-10 14:42:33 -04:00
parent 4e10c99c9f
commit 084b71e37d

View File

@@ -36,13 +36,21 @@ static void PrintErr(const char *a, const char *b) {
WriteFile(err, "\r\n", 2, &written, NULL);
}
/* Sets the same two mitigations as the GUI's secure wrapper, first thing on process start. */
/* Sets the same two mitigations as the GUI's wrapper, plus SetDefaultDllDirectories:
PreferSystem32Images alone did not stop a transitively-loaded DLL (cryptnet.dll, loaded
internally by crypt32.dll) from loading locally — confirmed via Process Monitor. */
static int ApplyMitigations(void) {
PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY sigPolicy;
PROCESS_MITIGATION_IMAGE_LOAD_POLICY imgPolicy;
ZeroMemory(&sigPolicy, sizeof(sigPolicy));
ZeroMemory(&imgPolicy, sizeof(imgPolicy));
/* Deliberately excludes LOAD_LIBRARY_SEARCH_APPLICATION_DIR — that's the directory this
vulnerability is about; it must never be part of the default search path. */
if (!SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32)) {
return 0;
}
if (!GetProcessMitigationPolicy(GetCurrentProcess(), ProcessSignaturePolicy, &sigPolicy, sizeof(sigPolicy))) {
return 0;
}
@@ -85,9 +93,8 @@ typedef CRYPT_PROVIDER_DATA *(WINAPI *PFN_WTHelperProvDataFromStateData)(HANDLE)
typedef CRYPT_PROVIDER_SGNR *(WINAPI *PFN_WTHelperGetProvSignerFromChain)(CRYPT_PROVIDER_DATA *, DWORD, BOOL, DWORD);
typedef DWORD(WINAPI *PFN_CertGetNameStringW)(PCCERT_CONTEXT, DWORD, DWORD, void *, LPWSTR, DWORD);
/* wintrust.dll/crypt32.dll are loaded dynamically, and only after ApplyMitigations() has
already succeeded, so PreferSystem32Images protects these loads (and everything they
transitively depend on) too — no static import table entry for either DLL exists. */
/* Loaded dynamically, after ApplyMitigations() has succeeded; LOAD_LIBRARY_SEARCH_SYSTEM32
forces this specific call to System32 too, on top of the process-wide default already set. */
static int VerifySignedBy(const wchar_t *filePath, const wchar_t *expectedSignerSubstring) {
HMODULE hWintrust, hCrypt32;
PFN_WinVerifyTrust pWinVerifyTrust;
@@ -100,8 +107,8 @@ static int VerifySignedBy(const wchar_t *filePath, const wchar_t *expectedSigner
LONG status;
int trusted, signerMatches;
hWintrust = LoadLibraryW(L"wintrust.dll");
hCrypt32 = LoadLibraryW(L"crypt32.dll");
hWintrust = LoadLibraryExW(L"wintrust.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
hCrypt32 = LoadLibraryExW(L"crypt32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!hWintrust || !hCrypt32) {
return 0;
}