Uncontrolled Search Path Element
Description
Uncontrolled Search Path Element occurs when a product uses a fixed or controlled search path to locate resources, but one or more elements of the search path are under the control of unintended actors. Unlike CWE-426 where the entire search path might be externally controlled, CWE-427 specifically involves cases where individual elements within the search path can be manipulated. Attackers place malicious resources in writable directories that are part of the search path, causing the application to load attacker-controlled code. This is commonly seen in DLL hijacking, Python/Node.js package dependency confusion, and PATH variable manipulation.
Risk
Uncontrolled search path vulnerabilities consistently lead to privilege escalation and arbitrary code execution. CVE-2025-7719 in GE Vernova's CIMPLICITY HMI/SCADA platform allows low-privileged users to escalate privileges in industrial control environments. CVE-2025-11772 in Synaptics Fingerprint Driver enables code execution with SYSTEM privileges through DLL planting in C:\ProgramData. Numerous products including Sony INZONE Hub, Panasonic AutoDownloader, FortiClient for Windows, and Siemens Altair Grid Engine have been affected. The "dependency confusion" attack variant affects software package management, allowing attackers to replace private packages with malicious public ones.
Solution
Remove writable directories from the search path. Use absolute paths for loading resources. Restrict write permissions on directories in the search path. On Windows, use SetDllDirectory("") and LOAD_LIBRARY_SEARCH_SYSTEM32 flags. For package managers, configure private registries and namespace prefixes to prevent dependency confusion. Validate that loaded resources are signed by trusted parties. Use application manifests to specify exact dependency locations. Audit applications for insecure DLL loading patterns.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Code Execution Attackers execute arbitrary code when their malicious resource is loaded from a controlled search path element. |
| Integrity | Scope: Privilege Escalation Code executes with the privileges of the vulnerable application, often SYSTEM level on Windows. |
| Availability | Scope: System Compromise Full system compromise enables persistent access and complete control. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: Searches current directory for DLLs
#include <windows.h>
int main() {
// LoadLibrary without full path searches:
// 1. Directory of the executable
// 2. Current directory (attacker can control!)
// 3. System directories
HMODULE hMod = LoadLibrary("plugin.dll");
// Attacker places malicious plugin.dll in current directory
// Application loads attacker's DLL before system DLL
}
// VULNERABLE: Configuration points to writable directory
// C:\ProgramData is writable by standard users!
const char* pluginPath = "C:\\ProgramData\\MyApp\\plugins\\";
# VULNERABLE: pip install searches public PyPI before private
# requirements.txt contains internal package name
# internal-package==1.0.0
# Attacker publishes "internal-package" on public PyPI
# with higher version number, gets installed instead
# VULNERABLE: sys.path includes writable directories
import sys
# If attacker can write to any directory in sys.path,
# they can plant malicious modules
print(sys.path) # Might include current directory "."
// VULNERABLE: Node.js module resolution
const helper = require('helper');
// Node searches:
// 1. Core modules
// 2. node_modules in current and parent directories
// 3. NODE_PATH directories
// If attacker can write to any searched node_modules,
// they can replace legitimate packages
// VULNERABLE: npm install with typosquatting
// package.json references "lodash" but attacker publishes "1odash"
Fixed Code
// SAFE: Load DLLs with absolute paths and secure flags
#include <windows.h>
#include <strsafe.h>
HMODULE load_dll_securely(const WCHAR* dllName) {
// Remove current directory from search path
SetDllDirectory(L"");
// For system DLLs, only search system directory
return LoadLibraryExW(
dllName,
NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32
);
}
HMODULE load_app_dll_securely(const WCHAR* dllName) {
WCHAR fullPath[MAX_PATH];
WCHAR appDir[MAX_PATH];
// Get application directory
GetModuleFileNameW(NULL, appDir, MAX_PATH);
PathRemoveFileSpecW(appDir);
// Build full path to DLL
StringCchPrintfW(fullPath, MAX_PATH, L"%s\\%s", appDir, dllName);
// Verify path is in expected location (no traversal)
WCHAR canonicalPath[MAX_PATH];
GetFullPathNameW(fullPath, MAX_PATH, canonicalPath, NULL);
if (wcsncmp(canonicalPath, appDir, wcslen(appDir)) != 0) {
SetLastError(ERROR_BAD_PATHNAME);
return NULL;
}
// Verify DLL signature (Windows Authenticode)
if (!verify_dll_signature(canonicalPath)) {
SetLastError(ERROR_INVALID_SIGNATURE);
return NULL;
}
return LoadLibraryExW(
canonicalPath,
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH
);
}
// SAFE: Install to protected directory only
// C:\Program Files\ (requires admin to write)
const WCHAR* pluginPath = L"C:\\Program Files\\MyApp\\plugins\\";
# SAFE: Configure pip to use private registry with priority
# pip.conf
# [global]
# index-url = https://private.example.com/simple/
# extra-index-url = https://pypi.org/simple/
# SAFE: Use namespace prefixes for internal packages
# Instead of: internal-package
# Use: companyname-internal-package
# SAFE: Pin exact versions with hashes
# requirements.txt
# internal-package==1.0.0 --hash=sha256:abc123...
# SAFE: Verify module source before importing
import importlib.util
import os
def safe_import(module_name, expected_path):
"""Import module only from expected path."""
full_path = os.path.join(expected_path, f"{module_name}.py")
# Verify path is within expected directory
real_path = os.path.realpath(full_path)
expected_real = os.path.realpath(expected_path)
if not real_path.startswith(expected_real + os.sep):
raise ImportError(f"Module {module_name} not in expected path")
if not os.path.exists(full_path):
raise ImportError(f"Module {module_name} not found")
spec = importlib.util.spec_from_file_location(module_name, full_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
// SAFE: Lock dependencies with package-lock.json
// Always commit package-lock.json to version control
// Use npm ci instead of npm install in CI/CD
// SAFE: Configure npm to use private registry
// .npmrc
// registry=https://private.example.com/
// @company:registry=https://private.example.com/
// SAFE: Use scoped packages for internal modules
// Instead of: require('internal-helper')
// Use: require('@company/internal-helper')
// SAFE: Verify package integrity
const crypto = require('crypto');
const fs = require('fs');
function verifyPackageIntegrity(packagePath, expectedHash) {
const fileBuffer = fs.readFileSync(packagePath);
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
if (hash !== expectedHash) {
throw new Error(`Package integrity check failed for ${packagePath}`);
}
return true;
}
// SAFE: Use absolute paths for require
const path = require('path');
const helper = require(path.join(__dirname, 'lib', 'helper'));
Exploited in the Wild
GE Vernova CIMPLICITY (GE Vernova, 2025)
CVE-2025-7719 in GE Vernova's CIMPLICITY HMI/SCADA platform allows low-privileged local users to escalate privileges through uncontrolled search path element exploitation, affecting industrial control systems with CVSS 7.0.
Synaptics Fingerprint Driver (Synaptics, 2025)
CVE-2025-11772 in Synaptics Fingerprint Driver loads DLLs from C:\ProgramData\Synaptics without validation, enabling local attackers to achieve SYSTEM-level code execution during driver installation.
Dependency Confusion Attacks (Multiple, 2021-Present)
Researchers demonstrated dependency confusion attacks affecting Microsoft, Apple, PayPal, and other major companies by publishing malicious packages with internal package names on public registries, executing code during package installation.
Tools to test/exploit
-
Dependency Confusion Checker — identify dependency confusion risks.
-
Procmon — monitor DLL loading behavior.
-
npm audit — identify vulnerable dependencies.
CVE Examples
-
CVE-2025-7719 — GE Vernova CIMPLICITY privilege escalation.
-
CVE-2025-11772 — Synaptics Fingerprint Driver DLL planting.
-
CVE-2021-23566 — nanoid package prototype pollution.
References
-
MITRE. "CWE-427: Uncontrolled Search Path Element." https://cwe.mitre.org/data/definitions/427.html
-
OWASP. "Dependency Confusion." https://owasp.org/www-project-web-security-testing-guide/