1.8 Executing External Programs Securely
1.8.1 Problem
Your Windows program needs to execute another program.
1.8.2 Solution
On Windows, use the
CreateProcess( ) API function to load and execute a new
program. Alternatively, use the CreateProcessAsUser(
) API function to load and execute a new
program with a primary access token other than the one in use by the
current program.
1.8.3 Discussion
The Win32 API provides several functions for executing new programs.
In the days of the Win16 API, the proper way to execute a new program
was to call WinExec( ). While this function still exists in the
Win32 API as a wrapper around CreateProcess( ) for
compatibility reasons, its use is deprecated, and new programs should
call CreateProcess( ) directly instead.
A powerful but extremely dangerous API function
that is popular among developers is ShellExecute(
). This function is implemented as a wrapper
around CreateProcess( ), and it does exactly what
we're about to advise against
doing with CreateProcess( )—but
we're getting a bit ahead of ourselves.
One of the reasons ShellExecute( ) is so popular
is that virtually anything can be executed with the API. If the file
to execute as passed to ShellExecute( ) is not
actually executable, the API will search the registry looking for the
right application to launch the file. For example, if you pass it a
filename with a .TXT extension, the filename
will probably start Notepad with the specified file loaded. While
this can be an incredibly handy feature, it's also a
disaster waiting to happen. Users can configure their own file
associations, and there is no guarantee that you'll
get the expected behavior when you execute a program this way.
Another problem is that because users can configure their own file
associations, an attacker can do so as well, causing your program to
end up doing something completely unexpected and potentially
disastrous.
The safest way to execute a new program is to use either
CreateProcess( ) or CreateProcessAsUser(
). These two functions share a very similar signature:
BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
BOOL CreateProcessAsUser(HANDLE hToken, LPCTSTR lpApplicationName,
LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
The two most important arguments for the purposes of proper secure
use of CreateProcess( ) or
CreateProcessAsUser( ) are
lpApplicationName and
lpCommandLine. All of the other arguments are well
documented in the Microsoft Platform SDK.
- lpApplicationName
-
Name of the program to execute. The program may be specified as an
absolute or relative path, but you should never specify the program
to execute in any way other than as a fully qualified absolute path
and filename. This argument may also be specified as
NULL, in which case the program to execute is
determined from the lpCommandLine argument.
- lpCommandLine
-
Any command-line arguments to pass to the new program. If there are
no arguments to pass, this argument may be specified as
NULL, but lpApplicationName and
lpCommandLine cannot both be
NULL. If lpApplicationName is
specified as NULL, the program to execute is taken
from this argument. Everything up to the first space is interpreted
as part of the filename of the program to execute. If the filename to
execute has a space in its name, it must be quoted. If
lpApplicationName is not specified as
NULL, lpCommandLine should not
contain the filename to execute, but instead contain only the
arguments to pass to the program on its command line.
By far, the biggest mistake that developers make when using
CreateProcess( ) or CreateProcessAsUser(
) is to specify lpApplicationName as
NULL and fail to enclose the program name portion
of lpCommandLine in quotes. As a rule, you should
never specify lpApplicationName as
NULL. Always specify the filename of the program
to execute in lpApplicationName rather than
letting Windows try to figure out what you mean from
lpCommandLine.
|