Monday, February 29, 2016

The Definitive Guide on Win32 to NT Path Conversion

Posted by James Forshaw, path’ological reverse engineer.


How cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Win32 APIs process file paths on Windows NT is a tale filled with backwards compatibility hacks, weird behaviour, and beauty†. Incorrect handling of Win32 paths can lead to security vulnerabilities. This blog post is to try and give a definitive* guide on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 different types of paths supported by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OS. I’m going to try and avoid discussion of quirks in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying filesystem implementations (such as NTFS streams and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 like), and instead focus on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Win32 to NT conversion layer.

The reason this blog post is needed at all is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 documentation for Win32 paths is incomplete relative to what’s actually implemented in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OS. If you find some code which takes an untrusted path and eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r does no sanitization at all or attempts to sanitize without fully understanding all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 possible scenarios cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re could be trouble. I was reminded when a colleague found an interesting path-handling vulnerability which I realised could be exploited furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r by abusing weird, and barely documented, behaviour in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Win32 path conversion code.

Caveats: this is based on analysis of Windows 8.1 and Windows 10. As some of this information isn’t documented cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation might change in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 future. Also this is based on calling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APIs directly, which a normal application is likely to do. Some or all of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se tricks might not work if passed into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Shell APIs or over an SMB share.

* I’ve done my best to make this definitive, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s always a chance I’ve missed something.
† As it’s commonly stated, beauty is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 eye of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beholder.

Background on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Relationship Between Win32 and NT Paths

The Windows kernel’s IO manager handles file paths differently from what is exposed through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public user-mode APIs such as CreateFile. The documentation for CreateFile and related functions generally point to this page, which describes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 different types of paths. Unfortunately it doesn’t really delve too deeply into how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 paths are converted; of course as Raymond Chen might describe cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 situation, it’s an implementation detail you shouldn’t rely on. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real world, if you’re dealing with paths which might come from untrusted sources, sometimes you have to understand how it works to determine what you need to protect against, so let’s get under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hood and delve into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation detail.

When you call CreateFile, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 API must do a conversion between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 many different types of paths Win32 supports and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying NT IO manager representation. Internally CreateFile calls a NTDLL export, RtlDosPathNameToRelativeNtPathName_U, which takes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Unicode Win32 path and returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appropriate NT path form. The fact that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function refers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se paths as Dos Paths gives cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 game away as to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir legacy. The function has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following prototype; if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 conversion is successful cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function will return TRUE:

typedef struct _RTL_RELATIVE_NAME {
 UNICODE_STRING RelativeName;
 HANDLE         ContainingDirectory;
 void*          CurDirRef;
} RTL_RELATIVE_NAME, *PRTL_RELATIVE_NAME;

BOOLEAN NTAPI RtlDosPathNameToRelativeNtPathName_U(
 _In_       PCWSTR DosFileName,
 _Out_      PUNICODE_STRING NtFileName,
 _Out_opt_  PWSTR* FilePath,
 _Out_opt_  PRTL_RELATIVE_NAME RelativeName
);

There are actually a few variants of this function. RtlDosPathNameToNtPathName_U, for example, won’t return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full relative path information (I’ll get on to what that means later). There’s also cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 functions suffixed with “WithStatus,” which instead of returning TRUE or FALSE return an NTSTATUS code that describes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reason for conversion failure.

There are 7 types of path that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Win32 API distinguishes between, and potentially does different things with. NTDLL has a function, RtlDetermineDosPathNameType_U, which, given a Unicode string will return you cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path type. We’ll go through each one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se types in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next section. The following prototype can be used to call this function:

enum RTL_PATH_TYPE {
 RtlPathTypeUnknown,
 RtlPathTypeUncAbsolute,
 RtlPathTypeDriveAbsolute,
 RtlPathTypeDriveRelative,
 RtlPathTypeRooted,
 RtlPathTypeRelative,
 RtlPathTypeLocalDevice,
 RtlPathTypeRootLocalDevice
};

RTL_PATH_TYPE NTAPI RtlDetermineDosPathNameType_U(_In_ PCWSTR Path);

Under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hood most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heavy lifting of converting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se different path types is done using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RtlGetFullPathName_U API. This takes a path string and performs conversion, canonicalization and resolving of current directory information. In most cases this function does not verify whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path exists (that’s why you’re opening it) but I’ll point out situations later where checks are made. We can call this externally with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following prototype; note in this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of bytes of path information converted:

ULONG NTAPI RtlGetFullPathName_U(
   _In_ PWSTR FileName,
   _In_ ULONG BufferLength,
   _Out_writes_bytes_(BufferLength) PWSTR Buffer,
   _Out_opt_ PWSTR *FilePart);

This API is actually exposed through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard Win32 API GetFullPathName so you don’t need to import it directly from NTDLL. I’ve put togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a simple tool to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se APIs to query what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 converted NT path is for each path type. I’ll use it as we go along. If you want to use it yourself you can find it at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of this blog post.

Types of DOS Path

Let’s look at each type of path in turn to see what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are and how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are converted into an NT path. I’ll also point out interesting behaviours as we go along and hopefully correct some assumptions about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 types of paths.

A common cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365me which will come up is canonicalization rules. I’ll explain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main rules now before digging into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 different paths and I’ll point out any odd behavior for each type. The implementation does cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following things to a path to make it canonical to pass through to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NT APIs.
  • Convert all forward slashes (character U+002F) to backslash path separator (character U+005C).
  • Collapse repeating runs of path separators into one.
  • Split up path elements and:
    • Remove elements where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name is only a single dot signifying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current directory.
    • Remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous path element where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name is two dots, if it’s not already at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path type. This is to allow relative paths referring to a parent.
  • If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last character is a path separator leave as is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final result.
  • Remove any trailing spaces or dots for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last path element, assuming that it isn’t a single or double dot name.
That last rule seems odd, but as we’ll see it really does do this for normal paths.

Drive Absolute

This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 simplest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 types of Win32 paths available and in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ory cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most unambiguous. Everyone should be familiar with this form, it contains a drive and at least one path element.
The following are all valid Drive Absolute paths with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 results of calling RtlGetFullPathName_U and RtlDosPathNameToRelativeNtPathName_U on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m (note that refers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 space character).

Dos Path
Full Path
NT Path
X:\ABC\DEF
X:\ABC\DEF
\??\X:\ABC\DEF
X:\
X:\
\??\X:\
X:\ABC\
X:\ABC\
\??\X:\ABC\
X:\ABC\DEF..
X:\ABC\DEF
\??\X:\ABC\DEF
X:/ABC/DEF
X:\ABC\DEF
\??\X:\ABC\DEF
X:\ABC\..\XYZ
X:\XYZ
\??\X:\XYZ
X:\ABC\..\..\..
X:\
\??\X:\

As specified in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canonicalization rules, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trailing space and dots have been removed. Note cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final row shows that you cannot create a relative path to replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive letter no matter how many parent directory references you use.

Drive Relative

These types of paths specify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive letter but do not follow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 colon with a path separator. For example C:ABC is a Drive Relative path. The implementation will replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive letter with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current directory set for that drive. The following process is used to determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current directory for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Drive Relative path.
  1. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive letter matches cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory drive letter cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n use that directory.
  2. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environment variable ‘=?:’ exists (where ? is replaced with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive letter) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path exists cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environment variable’s value.
  3. If all else fails just use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive root path (as in ?:\) and ensure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environment variable is updated to reflect that state.
Assuming that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory is set to X:\ABC, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Y drive has a variable for Y:\DEF and Z drive is not set to anything. There shouldn’t be anything unusual about any of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following results.

Dos Path
Full Path
NT Path
X:DEF\GHI
X:\ABC\DEF\GHI
\??\X:\ABC\DEF\GHI
X:
X:\ABC
\??\X:\ABC
X:DEF..
X:\ABC\DEF
\??\X:\ABC\DEF
Y:
Y:\DEF
\??\Y:\DEF
Z:
Z:\
\??\Z:\
X:ABC\..\XYZ
X:\ABC\XYZ
\??\X:\ABC\XYZ
X:ABC\..\..\..
X:\
\??\X:\

Of course if you know anything about Win32 programming you’d assume that you can only specify one current directory using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SetCurrentDirectory API. So how do any of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r environment variables get set? Well, in general cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y don’t; this is a feature implemented in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path conversion for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 benefit of command shell cmd.exe. The environment variable is set when you ‘cd’ to a new directory. These variables are hidden from most tools; however you can see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m using a debugger and dumping cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PEB (in this case using !peb in WinDBG).

environment_drives.PNG

We can see four different drives are set; however, what’s up with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first one ‘=::=::\’? To explain that, it’s natural to assume that drive “letters” can only be A through Z. It turns out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RtlGetFullPathName_U API does not enforce this requirement, although cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Explorer shell and command prompt almost certainly do. Therefore as long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second character of a path is a colon, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 conversion will treat it as a Drive Absolute or Drive Relative path. Of course if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DosDevices object directory doesn’t have an appropriate symbolic link it’s not going to do you much good.
weird_drive_letters.PNG
We can now explain why you find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 weird=::=:\’ entry in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environment block of most applications. The Explorer shell uses a special format to refer to shell objects. For example, pasting ‘::{20d04fe0-3aea-1069-a2d8-08002b30309d}’ into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 run dialog will open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 computer folder. Somewhere in Explorer something is passing one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se shell names to a file API, which is interpreting it as a Drive Relative path for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ‘:’ drive. As it doesn’t find an existing environment variable for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive it adds cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default environment variable. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 environment is inherited by default it’s migrated into ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes.

Rooted

A Rooted path is one which starts with a path separator. This creates a path rooted on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive currently set in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory. Effectively cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation prepends cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root drive (or UNC path if set) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n applies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 normal canonicalization rules for that path type. So assuming cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory is X:\ABC, we’d get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:

Dos Path
Full Path
NT Path
\ABC\DEF
X:\ABC\DEF
\??\X:\ABC\DEF
\
X:\
\??\X:\
\ABC\DEF..
X:\ABC\DEF
\??\X:\ABC\DEF
/ABC/DEF
X:\ABC\DEF
\??\X:\ABC\DEF
\ABC\..\XYZ
X:\XYZ
\??\X:\XYZ
\ABC\..\..\..
X:\
\??\X:\

Relative

These paths are relative to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory. The implementation determines a path is relative if it doesn’t start with a path separator and its second character is not a colon (indicating a drive path). The simplest way to think of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relative component is appended to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory path with a path separator added and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canonicalization rules are applied. Assuming cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 currently working directory is X:\XYZ, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n:

Dos Path
Full Path
NT Path
ABC\DEF
X:\XYZ\ABC\DEF
\??\X:\XYZ\ABC\DEF
.
X:\XYZ
\??\X:\XYZ
ABC\DEF..
X:\XYZ\ABC\DEF
\??\X:\XYZ\ABC\DEF
ABC/DEF
X:\XYZ\ABC\DEF
\??\X:\XYZ\ABC\DEF
..\ABC
X:\ABC
\??\X:\ABC
ABC\..\..\..
X:\
\??\X:\

Note that you can’t have an empty path (which includes just spaces or dots). If you want to refer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current directory you need to specify a single dot.

Relative paths also trigger anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r behavior when calling RtlDosPathNameToRelativeNtPathName_U, which can be used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Win32 APIs. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relative path is within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory, or one of its children, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation can return a file handle to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current working directory in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RTL_RELATIVE_NAME structure, which also contains only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relative name component. This can be passed as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RootDirectory handle in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OBJECT_ATTRIBUTES structure to NtCreateFile. This can be a performance win as it avoids cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object manager needing to parse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire path, work out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filesystem device, and call it with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 subpath to parse. Instead it can call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NTFS driver’s parse routine immediately. This handle is opened whenever cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current directory is changed and stored in a global variable in NTDLL. This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reason you can’t delete cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 directory an application has set as its current working directory as it holds a handle with no SHARE_DELETE option. We can see this behaviour by running cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tool and specifying a relative path.

relative_path.PNG

UNC Absolute

Universal Naming Convention (UNC) paths are a type which is pretty much only found on Windows (although arguably URIs replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir role on everything else). They’re used to access remote file systems, typically SMB but can be almost any implementation such as WebDAV (installed by default) or one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 many virtualization shared folder implementations. By convention a UNC path starts with two path separators, a server address (be it a domain name or an IP address), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of a share on that server. Finally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relative path to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resource you want is specified afterwards.

The conversion rules are pretty simple; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path is canonicalized as per cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 usual rules, although in this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root is considered to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 share name not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive letter; and finally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 leading path separators are replaced with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string ‘\??\UNC’, which routes to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Multiple UNC Provider (MUP) driver which handles dispatching cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appropriate remote file system provider. On to examples:

Dos Path
Full Path
NT Path
\\server\share\ABC\DEF
\\server\share\ABC\DEF
\??\UNC\server\share\ABC\DEF
\\server
\\server
\??\UNC\server
\\server\share
\\server\share
\??\UNC\server\share
\\server\share\ABC..
\\server\share\ABC
\??\UNC\server\share\ABC
//server/share/ABC/DEF
\\server\share\ABC\DEF
\??\UNC\server\share\ABC\DEF
\\server\share\ABC\..\XYZ
\\server\share\XYZ
\??\UNC\server\share\XYZ
\\server\share\ABC\..\..\..
\\server\share
\??\UNC\server\share

Local Device

A Local Device path is any path that begins with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sequence ‘\\.\’. This looks like a UNC path with a server name of ‘.’, however instead it’s used to directly escape to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DosDevices object manager directory. This directory contains things like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symbolic links for drive letters as well as for kernel drivers. It’s most commonly used to access devices such as COM ports and named pipes.

Dos Path
Full Path
NT Path
\\.\COM20
\\.\COM20
\??\COM20
\\.\pipe\mypipe
\\.\pipe\mypipe
\??\pipe\mypipe
\\.\X:\ABC\DEF..
\\.\X:\ABC\DEF
\??\X:\ABC\DEF
\\.\X:/ABC/DEF
\\.\X:\ABC\DEF
\??\X:\ABC\DEF
\\.\X:\ABC\..\XYZ
\\.\X:\XYZ
\??\X:\XYZ
\\.\X:\ABC\..\..\C:\
\\.\C:\
\??\C:\
\\.\pipe\mypipe\..\notmine
\\.\pipe\notmine
\??\pipe\notmine

Most things are as you’d expect. These paths are still canonicalized so trailing spaces and dots are removed. An odd behavior especially related to normal Drive Absolute or UNC Absolute paths is that you can completely remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first component of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path. This allows you to change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drive, change a local path to a UNC path, or even open a different named pipe as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying APIs have no knowledge of what’s being accessed so it can’t make any assumptions. I actually exploited this behaviour to create arbitrary named pipes from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Chrome sandbox awhile back (see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fixed issue here).

Note that \\localhost\xyz is not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same as \\.\xyz even though some APIs (say LogonUser) blur cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 distinction a little bit by specifying things like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local logon server as a single dot. The former accesses a UNC share on localhost over IP, whereas cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latter tries to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 access cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device name xyz.

You might make an assumption that you can only access special devices by specifying this form of path. If you read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page on path formats, though, you’ll find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following note, although it doesn’t explain why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's a restriction.

devices.PNG

The reason for this is due to legacy DOS support. For example, if you wanted to write data to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first COM port you could issue cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following command:

echo ATDT 2024561414 > COM1

The list of supported special case device names are as follows:
  • PRN
  • AUX
  • NUL
  • CON
  • LPT[1-9]
  • COM[1-9]
  • CONIN$
  • CONOUT$

Before we start, note that CONIN$ and CONOUT$ are not documented as being reserved. The LPT and COM names can take a number between 1 and 9 to refer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 9 configured ports. Actually even this isn’t strictly true, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying code passes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 character to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iswdigit library function and permits anything which is a digit based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call and not being ‘0’. If you test all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 16-bit characters as to whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’re considered digits it also includes characters U+00B2, U+00B3, and U+00B9, which are Superscript 2, Superscript 3, and Superscript 1 respectively. So if you’re desperate for a port name of COM² cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n go ahead.

This behavior was emulated in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path conversion process so that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se plain names are converted to Local Device paths, which end up with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct NT path. Now if just specifying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se paths explicitly was all that this process handled it would be annoying but not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 world. However it’s much worse. The conversion process actively tries to convert any path with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device name last, even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path is a Drive Absolute path. To make matters even worse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device name can have arbitrary trailing characters as long cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trailing characters are separated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device by a dot or a colon. The name can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n also have trailing spaces. Let’s look at some examples:

Dos Path
Full Path
NT Path
COM1
\\.\COM1
\??\COM1
X:\COM1
\\.\COM1
\??\COM1
X:COM1
\\.\COM1
\??\COM1
valid\COM1
\\.\COM1
\??\COM1
X:\notvalid\COM1
\\.\COM1
Error in Conversion
X:\COM1.blah
\\.\COM1
\??\COM1
X:\COM1:blah
\\.\COM1
\??\COM1
X:\COM1.blah
\\.\COM1
\??\COM1
\\.\X:\COM1
\\.\X:\COM1
\??\X:\COM1
\\abc\xyz\COM1
\\abc\xyz\COM1
\??\UNC\abc\xyz\COM1

Plenty of misbehavior here. Drive Absolute, Drive Relative, and Relative paths will be forcefully converted. Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 preceding drive path must be a valid directory, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 conversion to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NT path fails, although getting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full path works. Why it does cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check is beyond me as it seems to serve no actual purpose. Also note cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 removal of trailing suffixes, which can come in handy if something is actively trying to guard against this behavior. For example, if an application was mindful and was checking for a filename that matched one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reserved names you can just bypass that check by appending an arbitrary suffix.

Root Local Device

The final type is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Root Local Device path. This is any path that begins with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 characters \\?\ and acts as an escape into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object manager. It’s almost exactly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Local Device path type with one crucial difference: no canonicalization of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path is done. What this means is that forward slashes are not converted to backslashes, relative paths are not collapsed, and trailing spaces/dots are not removed. This lack of canonicalization has a number of useful properties when you can pass it to an application that is trying to secure paths.

Dos Path
Full Path
NT Path
\\?\X:\ABC\DEF
\\?\X:\ABC\DEF
\??\X:\ABC\DEF
\\?\X:\
\\?\X:\
\??\X:\
\\?\X:
\\?\X:
\??\X:
\\?\X:\COM1
\\?\X:\COM1
\??\X:\COM1
\\?\X:\ABC\DEF..
\\?\X:\ABC\DEF
\??\X:\ABC\DEF..
\\?\X:/ABC/DEF
\\?\X:\ABC\DEF
\??\X:/ABC/DEF
\\?\X:\ABC\..\XYZ
\\?\X:\XYZ
\??\X:\ABC\..\XYZ
\\?\X:\ABC\..\..\..
\\?\
\??\X:\ABC\..\..\..

Note that all canonicalization is skipped, including converting device names such as CON and replacing drive letters with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir current directory. But notice cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 discrepancy with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last four rows. While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting NT path has no canonicalization, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Full Path result has canonicalized cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 paths. This can only mean one thing: RtlDosPathNameToRelativeNtPathName_U must be special casing our path type and not calling RtlGetFullPathName_U on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. We can look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code to find out what it’s doing:

if (DosPath->Length > 8) {
 WCHAR* buffer = DosPath->Buffer;
 if (*buffer == '\\') {
   if (buffer[1] == '\\' || buffer[1] == '?')
       && buffer[2] == '?' && buffer[3] == '\\' ) {
       return RtlpWin32NtNameToNtPathName(DosPath, ...);
   }
 }
}
// Continue with processing.

So if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path starts with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 \\?\ prefix we instead call into RtlpWin32NtNameToNtPathName. This explains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 discrepancy. But wait, look again at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check, it isn’t just checking for \\?\, it also allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second character to be anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r ‘?’. What this means is that CreateFile or similar APIs also accept cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 form ‘\??\ABC’ as a valid Root Local Device path. Let’s just check to prove to ourselves it works:

weird_dos_prefix.PNG

If we run cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same set of paths as before we’ll see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 discrepancies (assume that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current drive is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 X: drive).

Dos Path
Full Path
NT Path
\??\X:\ABC\DEF
X:\??\X:\ABC\DEF
\??\X:\ABC\DEF
\??\X:\
X:\??\X:\
\??\X:\
\??\X:
X:\??\X:
\??\X:
\??\X:\COM1
X:\??\X:\COM1
\??\X:\COM1
\??\X:\ABC\DEF..
X:\??\X:\ABC\DEF
\??\X:\ABC\DEF..
\??\X:/ABC/DEF
X:\??\X:\ABC\DEF
\??\X:/ABC/DEF
\??\X:\ABC\..\XYZ
X:\??\X:\XYZ
\??\X:\ABC\..\XYZ
\??\X:\ABC\..\..\..
X:\
\??\X:\ABC\..\..\..

This type of path is just begging to be made an example of. If you look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type returned by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to RtlDetermineDosPathNameType_U and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full path, those APIs think it’s a rooted path, not a Root Local Device path. It would be easy to imagine a scenario where this could be abused. I’ll give one such example later.

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r thing to note is that using this type of path allows you to specify characters that would normally be considered illegal in a path. Every file system has some sort of limit on what characters it’s willing to accept as valid. This is usually for ease of use, such as not allowing NUL characters where your API is based on C-style terminated strings. The two most common file systems used on NT systems, NTFS and FAT, have considerably more limitations on valid characters as we can see in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following table, where anything red is banned from normal filenames.
ntfs_chars.png

While it’s true that an NTFS/FAT path on disk cannot contain illegal characters, at least added directly via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OS, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s nothing to stop a path from containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se characters as long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y don’t end up hitting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NTFS driver. For example, if you get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object manager into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mix (through redirecting via a mount point for example) only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following characters are illegal in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object manager:

obj_mgr.png

Quite a difference. Note that even NUL is valid as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NT kernel uses counted strings. The backslash is only invalid because without that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’d be no path separator. Obviously outside of NUL all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se characters can be put into a Rooted Local Device path.

Also cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canonicalization behavior works to our advantage. On systems such as Linux or OSX, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canonicalization is done during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process of opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. Therefore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 directory entries ‘.’ and ‘..’ really exist (or at least are faked sufficiently so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y exist). However, as we’ve seen on Windows, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canonicalization is done in user-mode before passing to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, and for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most part no verification is done on that path as to whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r it exists. So, for example, if you specify invalid characters as part of a string, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can be removed before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file is actually checked.
invalid_characters.PNG

As a final note, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 documentation for CreateFile, for example, explicitly states “In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ANSI version of this function, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Unicode version of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function and prepend "\\?\" to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path.” Therefore you’d make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 assumption that calling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ANSI version can’t take cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Rooted Local Device prefix, or if it can, it can’t support long paths.

This is demonstrably false. While you still need to specify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 \\?\ or similar prefixes ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise RtlDosPathNameToNtPathName gets unhappy, it’ll still work as no API checks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ANSI string before converting to Unicode (and subsequently calling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Unicode version of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 API). We can also understand why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 API is limited to 32,767 characters, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying NT UNICODE_STRING counted string structure represents cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of a string as a 16 bit integer. Because it stores it as a byte racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than character count, we can only store at most 215-1 characters, which just happens to be 32,767.

Bypassing Device and UNC Path Checks

Let’s finish up with an overview of how some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 quirks I’ve described can be used to attack real world applications eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r by tricking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code into opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wrong file/device or by bypassing path checks. Sometimes an application will allow you to specify a path, but try and restrict which types of resources you access. Common restrictions are blocking access to UNC paths and named pipes. Take for example cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 UNC case, where a really naïve check would be something like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:

BOOL IsUncPath(LPCWSTR Path) {
 if (wcslen(Path) > 2) {
   return (Path[0] == '\\' || Path[0] == '/')
       && (Path[1] == '\\' || Path[1] == '/');
 }
 return FALSE:
}

At least it checks for forward slashes (ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check is pretty easy to bypass). But it also excludes us from using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 \\?\UNC form as it still looks like a UNC path to this check function. So instead we can use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 \??\UNC prefix, which works just as well and circumvents cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check. Of course some checks try to be more clever. For example, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SHLWAPI function PathIsUNC is available on all Windows systems so it would make sense to call it and you’d assume it should handle all cases. It does have some weird behaviour though:

Path Specified
Result
\\abc\xyz
TRUE
C:\abc\xyz
FALSE
\\.\C:\abc\xyz
TRUE
\\?\C:\abc\xyz
FALSE
\\?\UNC\abc\xyz
TRUE
\??\UNC\abc\xyz
FALSE

Again I’ve highlighted cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 odd ones. The Local Device path is always considered a UNC path even though it isn’t. On cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hand, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Root Local Device path isn’t considered a UNC path unless it's followed by UNC. The alternative form of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Root Local Device path isn’t considered a UNC path eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r.

As an aside, even with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 \\?\ version you can bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check in PathIsUNC by exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object manager, specifically cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard GLOBALROOT symbolic link, so you use paths like:

Path Specified
Result
\\?\UNC\abc\xyz
TRUE
\\?\GLOBALROOT\??\UNC\abc\xyz
FALSE
\\?\GLOBALROOT\DosDevices\UNC\abc\xyz
FALSE
\\?\GLOBALROOT\Device\Mup\abc\xyz
FALSE
\\?\GLOBALROOT\Device\LanManRedirector\abc\xyz
FALSE
\\?\GLOBALROOT\Device\WebDavRedirector\abc\xyz
FALSE

The last two rows show you how to use this path to explicitly specify eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r SMB or WebDAV protocols. While we’re on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 subject, if you look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LanManRedirector or WebDavRedirector entries in WinObj you’ll find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m to be symbolic links to \Device\Mup\;NAME where NAME is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entry. Now think back to how UNC Absolute paths are converted from Win32 to NT: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 leading path separators are replaced with \??\UNC. As UNC itself is a symbolic link to \Device\Mup you can specify paths like:

Path
Final Result after Symbolic Link Resolving
\\;LanmanRedirector\evil.com\xyz
\Device\Mup\;LanmanRedirector\evil.com\xyz
\\;WebDavRedirector\evil.com\xyz
\Device\Mup\;WebDavRedirector\evil.com\xyz

This will no doubt confuse a parser into thinking you’re trying to access share evil.com on server ;LanmanRedirector instead of share xyz on evil.com. Fortunately this doesn’t seem to work in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Explorer shell, but it does work when passed to native APIs such as CreateFile. For extra bonus points it also breaks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 canonicalization; normally you can’t canonicalize above cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 share name but in this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation doesn’t realize xyz is a share name (it’s just as confused) so will allow it to be canonicalized away. There’s even more weirdness with UNC paths, but that’s perhaps for anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r time.

Conclusions

I hope you’ve seen that Win32 paths are massively more complex than it would seem even from just reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 documentation. There are so many quirks and weird behaviors it's very difficult to write code that validates all possible outcomes. Converting everything to NT paths helps slightly as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are less prone to misbehavior, but even cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n through symbolic link abuse or changing drive letters it's still possible to be confused. If you ever encounter an application trying to validate a Win32 path, be very skeptical and try and use some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se techniques to bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 checks.

Example Program

Here’s cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 simple example program which uses all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 various API functions on a string passed on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 command line. This will allow you to test each example to prove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 results. I’ll use C# as it’s a lot easier to call into NTDLL functions without needing a library or messing around with GetProcAddress. As a bonus every version of Windows since Vista has a version of .NET installed by default which includes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 C# CSC compiler so no need to install a C compiler or Python.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
 [StructLayout(LayoutKind.Sequential)]
 struct UNICODE_STRING
 {
   public ushort Length;
   public ushort MaximumLength;
   public IntPtr Buffer;

   public override string ToString()
   {
     if (Buffer != IntPtr.Zero)
       return Marshal.PtrToStringUni(Buffer, Length / 2);
     return "(null)";
   }
 }

 [StructLayout(LayoutKind.Sequential)]
 class RTL_RELATIVE_NAME
 {
   public UNICODE_STRING RelativeName;
   public IntPtr ContainingDirectory;
   public IntPtr CurDirRef;
 }

 [DllImport("ntdll.dll", CharSet = CharSet.Unicode)]
 static extern int RtlDosPathNameToRelativeNtPathName_U_WithStatus(
   string DosFileName,
   out UNICODE_STRING NtFileName,
   out IntPtr ShortPath,
   [Out] RTL_RELATIVE_NAME RelativeName
   );

 enum RTL_PATH_TYPE
 {
   RtlPathTypeUnknown,
   RtlPathTypeUncAbsolute,
   RtlPathTypeDriveAbsolute,
   RtlPathTypeDriveRelative,
   RtlPathTypeRooted,
   RtlPathTypeRelative,
   RtlPathTypeLocalDevice,
   RtlPathTypeRootLocalDevice
 }

 [DllImport("ntdll.dll", CharSet = CharSet.Unicode)]
 static extern RTL_PATH_TYPE RtlDetermineDosPathNameType_U(string Path);

 [DllImport("ntdll.dll", CharSet = CharSet.Unicode)]
 static extern int RtlGetFullPathName_UEx(
   string FileName,
   int BufferLength,
   [Out] StringBuilder Buffer,
   IntPtr FilePart,
   out int FinalLength);

 [DllImport("ntdll.dll")]
 static extern int RtlNtStatusToDosError(int NtStatus);
 
 static void PrintStatus(int status)
 {
   Console.WriteLine("Error:        {0}",
     new Win32Exception(RtlNtStatusToDosError(status)).Message);
 }

 static void ConvertPath(string path)
 {
   Console.WriteLine("Converting:   '{0}'", path);
   UNICODE_STRING ntname = new UNICODE_STRING();
   IntPtr filename = IntPtr.Zero;
   RTL_RELATIVE_NAME relative_name = new RTL_RELATIVE_NAME();
   int status = RtlDosPathNameToRelativeNtPathName_U_WithStatus(
                   path,
                   out ntname,
                   out filename,
                   relative_name);
   if (status == 0)
   {
     Console.WriteLine("To:           '{0}'",
       ntname);
     Console.WriteLine("Type:         {0}",
       RtlDetermineDosPathNameType_U(path));
     Console.WriteLine("FileName:     {0}",
       Marshal.PtrToStringUni(filename));
     if (relative_name.RelativeName.Length > 0)
     {
       Console.WriteLine("RelativeName: '{0}'",
         relative_name.RelativeName);
       Console.WriteLine("Directory:    0x{0:X}",
         relative_name.ContainingDirectory.ToInt64());
       Console.WriteLine("CurDirRef:    0x{0:X}",
         relative_name.CurDirRef.ToInt64());
     }
   }
   else
   {
     PrintStatus(status);
   }

   int length = 0;
   StringBuilder builder = new StringBuilder(260);
   status = RtlGetFullPathName_UEx(
     path,
     builder.Capacity * 2,
     builder,
     IntPtr.Zero,
     out length);
   if (status == 0)
   {
     Console.WriteLine("FullPathName: '{0}'",
       builder.ToString());
   }
   else
   {
     PrintStatus(status);
   }
 }
 
 static void Main(string[] args)
 {
   if (args.Length < 1)
   {
     Console.WriteLine("Usage: ConvertDosPathToNtPath DosPath");
   }
   else
   {
     ConvertPath(args[0]);      
   }
 }
}

Let’s just try it and see if it works:


tool_test.PNG