Full Disclosure mailing list archives

Defense in depth -- the Microsoft way (part 36): CWE-428 or fun with unquoted paths


From: "Stefan Kanthak" <stefan.kanthak () nexgo de>
Date: Sun, 15 Nov 2015 07:01:03 +0100

Hi @ll,

on 2014-11-14 once again a Microsoft employee tried to downplay
CWE-428 <https://cwe.mitre.org/data/definitions/428.html>:

<http://blogs.msdn.com/b/aaron_margosis/archive/2014/11/14/it-rather-involved-being-on-the-other-side-of-this-airtight-hatchway-unqu
oted-service-paths.aspx>

In standard installations of Windows NT users are per default
on the wrong side of this (not so) "airtight hatchway":
the user account(s) created during setup is/are member(s) of
the "Administrators" group, and the security theatre named
"user account control" is NO security boundary!


Despite this, unprivileged users can create a directory
%SystemDrive%\Program [*] on ALL versions of Windows NT.

From <https://support.microsoft.com/en-us/kb/812486>:

| * 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.


On Windows NT5.x (Windows Embedded POSReady 2009 is in extended
support until April 2019)
    CreateProcess*(NULL, "C:\\Program Files\\Internet Explorer\\iexplore.exe", ...)
as well as
    CreateProcess*(NULL, "C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe", ...)
return ERROR_ACCESS_DENIED if the directory C:\Program exists.

On Windows NT6.x the directory C:\Program is but ignored/skipped
and the command line executed successfully.

On Windows NT5.x AND Windows NT6.x
    CreateProcess*(NULL, "C:\\Program Files\\Internet Explorer\\iexplore.exe", ...)
return ERROR_ACCESS_DENIED if the directory
"C:\Program Files\Internet" (yes, you need administrative rights
to create it, but that's not the point here) exists.


Now remove the directories "C:\Program Files\Internet" and
"C:\Program" and create the file "C:\Program Files\Internet.exe":
    COPY "%COMSPEC%" "C:\Program Files\Internet.exe"
(yes, you need administrative rights to create it, but that's
still not the point here).

On Windows NT5.x AND Windows NT6.x
    CreateProcess*(NULL, "C:\\Program Files\\Internet Explorer\\iexplore.exe", ...)
executes the file "C:\Program Files\Internet.exe".

Create the directory "C:\Program Files\Internet" and retry:
    CreateProcess*(NULL, "C:\\Program Files\\Internet Explorer\\iexplore.exe", ...)
does NOT execute the file "C:\Program Files\Internet.exe" but
"C:\Program Files\Internet Explorer\iexplore.exe" instead, i.e.
the presence of the directory "C:\Program Files\Internet" lets
Windows skip/ignore a file "C:\Program Files\Internet.exe"!


Does this work for the file "C:\Program.exe" too?
After
    COPY "%COMSPEC%" "C:\Program.exe"
both
    CreateProcess*(NULL, "C:\\Program Files\\Internet Explorer\\iexplore.exe", ...)
    CreateProcess*(NULL, "C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe", ...)
execute "C:\Program.exe" on ALL versions of Windows NT.
Create the directory "C:\Program" and retry:
    CreateProcess*(NULL, "C:\\Program Files\\Internet Explorer\\iexplore.exe", ...)
return ERROR_ACCESS_DENIED on Windows NT5.x, but execute the
command line on Windows NT6.x


PS: if 8.3 filename creation is enabled a user with privilege
    SE_RESTORE_NAME might just create short names (see
    <https://msdn.microsoft.com/en-us/library/aa365543.aspx> or
    <https://technet.microsoft.com/en-us/library/bb490642.aspx>):
    PROGRAM      for "C:\Program Files",
    PROGRAM.EXE  for "C:\<arbitrary_executable>",
    INTERNET     for "C:\Program Files\Internet Files" or
    INTERNET.EXE for "C:\Program Files\<arbitrary_executable>".


JFTR: the documentation of CreateProcess*() behaviour for
      unquoted command lines is incomplete:

      <https://msdn.microsoft.com/en-us/library/ms682425.aspx>
      <https://msdn.microsoft.com/en-us/library/ms682429.aspx>
      <https://msdn.microsoft.com/en-us/library/ms682431.aspx>
      <https://msdn.microsoft.com/en-us/library/ms682434.aspx>

      | lpCommandLine
      ...
      | 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:
                                    (quotes added for clarity)
      |
      | "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"

      Neither the 4 other possibilities:
      "C:\Program" files\sub dir\program name
      "C:\Program files\sub" dir\program name
      "C:\Program files\sub dir\program" name
      "C:\Program files\sub dir\program name"
      (executables dont need to have a file extension at all!)
      nor the precedence of directories "C:\<something>" over
      files "C:\<something>.exe" are documented!


JFTR: LoadLibrary*("") searches for a file .DLL and loads it.

      Executables also dont need to have a filename at all.-P


stay tuned
Stefan Kanthak


[*] finding the appropriate directory name for non-english versions
    of Windows NT3.x, NT4.x and NT5.x (where unprivileged users can
    but create a file %SystemDrive%\Program [*] as well as
    %SystemDrive%\Program.exe which will be executed) is left as an
    exercise to the reader.


PS: If you want to play further:
    see the documentation of NTFS reparse points
    <https://msdn.microsoft.com/en-us/library/aa365503.aspx>:

    | A file or directory can contain a reparse point, which is a
    | collection of user-defined data.
    ...
    | 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 not found, the file open operation
    | fails.

    and add this "collection of user-defined data" to the just
    created directory %SystemDrive%\Program:

    - users with the privilege SE_CREATE_SYMBOLIC_LINK_NAME can
      create a symbolic link;

    - ALL users can create a junction (or mount point), an NFS
      symbolic link, a WIM or SIS reparse point, etc.


    (Un)fortunately the file system filter driver for symbolic links
    only handles reparse points from file to file and directory to
    directory; it fails on reparse points from file to directory and
    from directory to file.

    The file system filter driver for junctions (and mount points)
    only handles reparse points from directory to directory or from
    directory to drive; it fails on reparse points with all other
    combinations.


_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: http://seclists.org/fulldisclosure/


Current thread: