[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Defense in depth -- the Microsoft way (part 29): contradicting, ambiguous, incomplete documentation



Hi @ll,

the MSDN documents the BRAINDEAD behaviour of the functions
CreateProcess() <https://msdn.microsoft.com/en-us/library/ms682425.aspx>,
CreateProcessAsUser() <https://msdn.microsoft.com/en-us/library/ms682429.aspx>
CreateProcessWithLogonW() <https://msdn.microsoft.com/en-us/library/ms682431.aspx>
CreateProcessWithTokenW() <https://msdn.microsoft.com/en-us/library/ms682434.aspx>
for an unquoted "long" pathname of the executable in command lines:

| [...] the module name must be the first white space-delimited token in
| the lpCommandLine string. If you are using a long file name that contains
| a space, use quoted strings to indicate where the file name ends and the
| arguments begin; otherwise, the file name is ambiguous.
| For example, consider the string "c:\program files\sub dir\program name".
| This string can be interpreted in a number of ways. The system tries
| to interpret the possibilities in the following order:         ~~~~~
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| c:\program.exe files\sub dir\program name
| c:\program files\sub.exe dir\program name
| c:\program files\sub dir\program.exe name
| c:\program files\sub dir\program name.exe
...
| [...] the first white space-delimited token of the command line
| specifies the module name. If you are using a long file name that
| contains a space, use quoted strings to indicate where the file name
| ends and the arguments begin [...] If the file name does not contain
| an extension, .exe is appended. [...] if the file name contains a path,
| .exe is not appended.                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~~~~~~~~~~~~~~~~~~~~~


1. self-contradictory/ambiguous:

   If the second underlined clause of the documentation were correct,
   and the usual definition of path(name) ['] is assumed (so "c:\",
   "c:\program files\sub" and "c:\program files\sub dir\program"
   qualify as pathnames), the interpretation of the string
      "c:\program files\sub dir\program name"
   given above would be wrong.

   The interpretation given above but matches the observed behaviour
   of the CreateProcess*() functions, therefore the underlined clause
   of the documentation is WRONG and should be removed!


2. incomplete/ambiguous:

   the documentation does not specify what "tries to interpret the
   possibilities" exacly means: using the example given above, is
   CreateProcess*() expected to skip
   "c:\program.exe", "c:\program files\sub.exe" and/or
   "c:\program files\sub dir\program.exe" if they exist but are not
   executable

   a) due to invalid file contents?

      Observed behaviour:
      CreateProcess*() tries to execute c:\program.exe etc. and returns
      ERROR_BAD_EXE_FORMAT, ERROR_EXE_MARKED_INVALID,
      ERROR_INVALID_EXE_SIGNATURE, etc.

   b) due to insufficient permissions?

      Observed behaviour:
      CreateProcess*() tries to execute c:\program.exe etc. and returns
      ERROR_ACCESS_DENIED!

   c) because they are an unsupported or unknown [²] reparse point?

      Observed behaviour:
      CreateProcess*() tries to execute c:\program.exe etc. and returns
      ERROR_CANT_ACCESS_FILE, ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND,
      etc.

   d) because they are a directory (including directories overlaid with
      a reparse point), a mount point, a junction to a directory or a
      symlink to a directory?

      Observed behaviour:
      in contradiction to MSKB 812486 [³] CreateProcess*() does not
      try to execute a directory c:\program.exe etc. and skips it.

      Skipping over directories or reparse points targetting
      directories is but surprising behaviour, since
      CreateProcess*("<directory>", NULL, ...) and
      CreateProcess*(NULL, "<directory>", ...) return
      (as expected) ERROR_ACCESS_DENIED!


3. incomplete/surprising:

   The observed behaviour for
   CreateProcess(NULL, "C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe", ...)
   is: CreateProcess*() tries to execute "C:\Program.exe" and
   "C:\Program Files (x86)\Internet.exe", but NOT
   "C:\Program Files.exe"

   WTF?
   Why is "C:\Program Files.exe" NOT a "possibility to try"?


4. incomplete:

   when CreateProcess*() is called (for example) with the command
   line "c:\program files\sub dir\program name" and plays try&error,
   the executed application does NOT receive the original command line,
   but a MODIFIED command line:

   - "c:\program files\sub.exe" dir\program name
   - "c:\program files\sub dir\program.exe" name
   - "c:\program files\sub dir\program.exe name.exe"

   i.e. the pathname of the found executable gets quoted if it contains
   a space.

   The documentation of the function GetCommandLine()
   <https://msdn.microsoft.com/en-us/library/ms683156.aspx>
   but misses this completely!


Stay tuned!


regards
Stefan Kanthak

['] as soon as a name contains a single '\' or ':' it is a pathname!
    Cf. <https://msdn.microsoft.com/en-us/library/aa365247.aspx>

[²] <https://msdn.microsoft.com/en-us/library/aa365503.aspx>:

    | When the file system opens a file

    or directory                        (obvious omission added)

    | with a reparse point, it attempts to find the file system filter
    | associated with the data format identified by the reparse tag.
    | If a file system filter is found, the filter processes the file

    or directory                        (obvious omission added)

    | as directed by the reparse data. If a file system filter is not
    | found, the file open operation fails.

[³] <https://support.microsoft.com/kb/812486/en-us>:

    | * There is a file or folder on your computer's hard disk that
    |   has the same name as a file or folder in the path to the
    |   service's executable file.
    |
    | For example, if the path of the executable file for a service
    | is C:\Program Files\MyProgram\MyService.exe, and if a folder
    | that is named C:\Program also exists on your hard disk, Windows
    | locates the C:\Program folder on your hard disk before the
    | C:\Program Files\MyProgram\My Service.exe file, and then tries
    | to run it.