Untrusted Search Path

Description

Untrusted Search Path occurs when a product searches for critical resources such as libraries or executables using an externally-supplied search path that can point to resources not under the product's direct control. Attackers can manipulate the search path or place malicious resources in locations that are searched before the legitimate resource locations. When the product loads these malicious resources, it executes attacker-controlled code with the privileges of the vulnerable application. This is commonly exploited through DLL hijacking on Windows, where applications load malicious DLLs from the current directory or other attacker-controlled locations.

Risk

Untrusted search path vulnerabilities enable privilege escalation and arbitrary code execution. CVE-2025-12793 in ASUS ASCI allows local attackers to execute code by placing malicious DLLs in paths searched by AsusSoftwareManagerAgent. CVE-2024-6769 combines drive remapping with activation context poisoning for Windows DLL hijacking. CVE-2024-14012 in Revenera InstallShield enables privilege escalation through MPR.dll hijacking. FortiClient Windows is also vulnerable. These attacks are particularly dangerous because they can escalate from low-privileged local access to SYSTEM-level code execution, and can sometimes be triggered remotely via SMB or WebDAV shares.

Solution

Always use fully-qualified paths when loading libraries, executables, or other resources. Remove the current directory from the DLL search path using SetDllDirectory("") on Windows. Use secure library loading flags like LOAD_LIBRARY_SEARCH_SYSTEM32. Hard-code search paths to known-safe system directories. Validate that loaded resources come from expected locations. Implement proper ACLs to prevent unauthorized users from writing to directories in the search path. Use application manifests to specify exact DLL versions and locations.

Common Consequences

ImpactDetails
Access ControlScope: Code Execution

Attackers execute arbitrary code when their malicious resource is loaded by the vulnerable application.
IntegrityScope: Privilege Escalation

Code runs with the privileges of the vulnerable application, often SYSTEM or administrator level.
ConfidentialityScope: System Compromise

Successful exploitation provides persistent access and ability to access sensitive data.

Example Code + Solution Code

Vulnerable Code

// VULNERABLE: Loading DLL without full path
#include <windows.h>

void load_plugin() {
    // Windows searches: app dir, current dir, system dir, etc.
    // Attacker can plant malicious helper.dll in current directory
    HMODULE hLib = LoadLibrary("helper.dll");

    if (hLib) {
        // Now running attacker's code with app privileges!
        typedef void (*InitFunc)();
        InitFunc init = (InitFunc)GetProcAddress(hLib, "Initialize");
        if (init) init();
    }
}
// VULNERABLE: Executing program without full path
#include <cstdlib>

void run_utility() {
    // system() uses PATH environment variable
    // Attacker can modify PATH or place malicious utility in searched dir
    system("utility.exe --process");
}

// VULNERABLE: Python module loading
// If PYTHONPATH is attacker-controlled, malicious modules can be loaded
import helper  // Could load attacker's helper.py
// VULNERABLE: Loading native library without full path
public class NativeWrapper {
    static {
        // Java searches java.library.path, which may include attacker paths
        System.loadLibrary("nativehelper");  // Loads nativehelper.dll
    }

    public native void processData(byte[] data);
}

Fixed Code

// SAFE: Load DLL with full path and secure flags
#include <windows.h>
#include <libloaderapi.h>
#include <shlwapi.h>

HMODULE load_library_safely(const char* dllName) {
    // Remove current directory from DLL search path
    SetDllDirectory("");

    // Build full path to system directory
    char systemPath[MAX_PATH];
    GetSystemDirectory(systemPath, MAX_PATH);
    PathAppend(systemPath, dllName);

    // Load only from system directory
    HMODULE hLib = LoadLibraryEx(
        systemPath,
        NULL,
        LOAD_LIBRARY_SEARCH_SYSTEM32  // Only search system32
    );

    return hLib;
}

// SAFE: Load application DLL with explicit path
HMODULE load_app_library(const char* dllName) {
    char appPath[MAX_PATH];

    // Get the application's directory
    GetModuleFileName(NULL, appPath, MAX_PATH);
    PathRemoveFileSpec(appPath);
    PathAppend(appPath, dllName);

    // Verify the path is within expected directory
    if (!path_is_trusted(appPath)) {
        return NULL;
    }

    return LoadLibraryEx(appPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
}

int path_is_trusted(const char* path) {
    // Verify path starts with expected application directory
    char expectedBase[MAX_PATH];
    GetModuleFileName(NULL, expectedBase, MAX_PATH);
    PathRemoveFileSpec(expectedBase);

    // Normalize and compare paths
    char normalizedPath[MAX_PATH];
    GetFullPathName(path, MAX_PATH, normalizedPath, NULL);

    return strncmp(normalizedPath, expectedBase, strlen(expectedBase)) == 0;
}
// SAFE: Execute programs with full paths
#include <windows.h>
#include <string>

void run_utility_safely() {
    // Build full path to the utility
    char systemPath[MAX_PATH];
    GetSystemDirectory(systemPath, MAX_PATH);

    std::string fullPath = std::string(systemPath) + "\\utility.exe";

    // Use CreateProcess instead of system() for more control
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;

    CreateProcess(
        fullPath.c_str(),
        NULL,  // Command line
        NULL,  // Process security
        NULL,  // Thread security
        FALSE, // Inherit handles
        0,     // Creation flags
        NULL,  // Environment (use parent's)
        NULL,  // Current directory (use parent's)
        &si,
        &pi
    );

    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

// SAFE: Python with explicit module paths
// Set PYTHONPATH explicitly in controlled startup script
// Use importlib with explicit paths
import importlib.util
import os

def load_trusted_module(module_name, expected_dir):
    module_path = os.path.join(expected_dir, f"{module_name}.py")

    # Verify module is in expected location
    real_path = os.path.realpath(module_path)
    if not real_path.startswith(os.path.realpath(expected_dir)):
        raise SecurityError(f"Module path traversal detected: {module_path}")

    spec = importlib.util.spec_from_file_location(module_name, module_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module
// SAFE: Load native library with full path
public class SecureNativeWrapper {
    static {
        String libPath;

        // Determine platform-specific library name and location
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.contains("win")) {
            libPath = System.getenv("ProgramFiles") +
                      "\\MyApp\\lib\\nativehelper.dll";
        } else {
            libPath = "/usr/lib/myapp/libnativehelper.so";
        }

        // Verify the path is within expected directory
        File libFile = new File(libPath);
        try {
            String canonicalPath = libFile.getCanonicalPath();
            if (!canonicalPath.startsWith(getExpectedLibDir())) {
                throw new SecurityException("Library path outside expected directory");
            }
        } catch (IOException e) {
            throw new RuntimeException("Cannot verify library path", e);
        }

        // Load with explicit full path
        System.load(libPath);
    }

    private static String getExpectedLibDir() {
        // Return the expected base directory for native libraries
        return System.getenv("ProgramFiles") + File.separator + "MyApp";
    }

    public native void processData(byte[] data);
}

Exploited in the Wild

ASUS Software Manager Agent (ASUS, 2025)

CVE-2025-12793 in ASUS ASCI AsusSoftwareManagerAgent allows local attackers to execute arbitrary code by placing malicious DLLs in paths searched by the vulnerable component, affecting versions before v3.1.49.0.

Windows Drive Remapping Attack (Microsoft, 2024)

CVE-2024-6769 combines drive remapping with activation context poisoning to achieve DLL hijacking on Windows systems, demonstrating sophisticated exploitation of untrusted search paths.

Revenera InstallShield (Revenera, 2024)

CVE-2024-14012 in InstallShield 2023 R1 enables privilege escalation when Setup.exe loads MPR.dll from an insecure directory, allowing local administrators to gain higher privileges.


Tools to test/exploit

  • Procmon — monitor file system activity to identify DLL search order.

  • DLL Hijack Auditor — identify DLL hijacking opportunities.

  • Robber — DLL hijacking vulnerability scanner.


CVE Examples


References

  1. MITRE. "CWE-426: Untrusted Search Path." https://cwe.mitre.org/data/definitions/426.html

  2. Microsoft. "Dynamic-Link Library Security." https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-security