A customer reported that the SearchPath
function was returning an NT-style path, which is not something their program (an IIS CGI handler) could deal with. Why are they getting an NT-style path?
The problem was not reproducible when run under a debugger, so we had to resort to tracing and logging.
One theory was that IIS was putting an NT-style path in the PATH
, and that’s where it was coming from. But logging the value of the PATH
environment variable showed no weird \\?\
directories.
I recalled that when you have eliminated the impossible, whatever remains, however improbable, must be the truth. The SearchPath
function looks in the directories specified in the PATH
environment variable, and if the item can’t be found in any of those directories, then it looks in the current directory.
We have ruled out the PATH
environment variable.
Therefore, the current directory must be the one with the NT-style path.
It turns out that, for security reasons, IIS runs CGI programs via their NT path, and also uses an NT path as the current directory. It so happened that the file was found in the current directory, so that’s what SearchPath
returned.
We can even run a test program to check the theory.
#include <windows.h> #include <stdio.h> int __cdecl main() { SetCurrentDirectoryW(L"\\\\?\\C:\\Users"); wchar_t buffer[256]; wchar_t* file; if (SearchPath(nullptr, L"Public", nullptr, 256, buffer, &file)) { printf("%ls\n", buffer); } }
Run this program and it prints
\\?\C:\Users\Public
Okay, that explains the problem. But how do they fix it?
You could try to convert the NT path to a Win32-style path before processing it. But really, since this is a CGI program, you probably shouldn’t be grabbing files from the current directory in the first place. You aren’t really in control of your current directory, and the fact that you’re looking up things in the current directory means that an attacker might be able to pass paths like ..\..\wwwroot\config\jackpot.xml
, and steal files from anywhere on your system.
So the real issue is that their program is somehow reliant upon the current directory, and they should fix it to remove that dependency.
I think it is OK to read files from the current directory, as long as the paths are properly escaped and path traversal is addressed (i.e. removing “..\”)
I read this as the application looking for a known file not realising that someone was trying to hijack it by dropping a substitute in the current directory and being protected by the given behaviour of IIS, so that the application's dependence on the current directory was inadvertent.
Best practice would appear to depend on why the application needs to use SearchPath, but maybe they could use the API to prefer the to the...
Enforcing the “safe process search mode” won’t help
1. if an adversary can drop a substitute in a directory listed in %PATH% before the directory where the searched for executable is located;
2. if an adversary can drop an arbitrary/unique named executable in a directory listed in %PATH%, including the CWD, and feeds this arbitrary/unique name to SearchPath() or CreateProcess() or …
Better call SetCurrentDirectory(“\\\\.\\nul\\”) instead of SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE)
These are not NT-style paths, depending on definition.
These are Win32 paths.
NT paths are like \??\.
Granted you said “NT style”.
These are perfectly valid, heavily documented, etc.
Wonky, granted.
Just as an aside for any C++ programmer reading the comments who doesn’t already know this, In C++11+ you can write raw string literals like
LR"(\\?\C:\Users)"
for Windows paths, avoiding the need to escape backslashes.“… when you have eliminated the -improbable- …” -> that should read “… when you have eliminated the *impossible* …”, shouldn’t it?
Indeed, fixed.