This document summarizes a presentation about abusing symbolic links on Windows. It discusses how symbolic links are implemented for object manager objects, registry keys, and NTFS files. It outlines different classes of vulnerabilities like resource overwrite, information disclosure, and time-of-check/time-of-use issues. Examples are given of vulnerabilities in Internet Explorer and the Windows Task Scheduler. Exploitation techniques like using object manager symlinks, mount points, and OPLOCKs are also described.
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Abusing Symlinks on Windows
1. A Link to the Past
Abusing Symbolic Links on Windows
James Forshaw @tiraniddo
1
2. James Forshaw @tiraniddo
Obligatory Background Slide
● Researcher in Google’s
Project Zero team
● Specialize in Windows
○ Especially local privilege
escalation
● Never met a logical
vulnerability I didn’t like
https://www.flickr.com/photos/barretthall/2478623520/
2
3. James Forshaw @tiraniddo
What I’m Going to Talk About
● Implementation of Symbolic Links on Windows
● Exploitable Bug Classes
● Example vulnerabilities
● Offensive exploitation tricks
3
6. James Forshaw @tiraniddo
Resource Creation or Overwrite
6
Privileged Application
pathtoresource
Write to
resource
sensitivepath
Symbolic Link
7. James Forshaw @tiraniddo
Information Disclosure
7
Privileged Application
pathtoresource
Read
Resource
sensitivepath
Symbolic Link
Disclosure
Unprivileged Application
8. James Forshaw @tiraniddo
Time of Check/Time of Use
8
Privileged Application
pathtoresource
Check and
use
Resource
validfile
Check
Symbolic Link
maliciousfile
Use Symbolic
Link
9. James Forshaw @tiraniddo
History of Windows Symbolic Links
Windows NT 3.1 - July 27 1993
Object Manager Symbolic Links
Registry Key Symbolic Links
9
10. James Forshaw @tiraniddo
History of Windows Symbolic Links
Windows NT 3.1 - July 27 1993
Object Manager Symbolic Links
Registry Key Symbolic Links
Windows 2000 - Feb 17 2000
NTFS Mount Points and
Directory Junctions
10
11. James Forshaw @tiraniddo
History of Windows Symbolic Links
Windows NT 3.1 - July 27 1993
Object Manager Symbolic Links
Registry Key Symbolic Links
Windows 2000 - Feb 17 2000
NTFS Mount Points and
Directory Junctions
Windows Vista - Nov 30 2006
NTFS Symbolic Links
11
17. James Forshaw @tiraniddo
Object Manager Reparsing
NtOpenSemaphore ObOpenObjectByName ObpLookupObjectName
17
Parsing Name
MyObjectsGlobalMySema
Current Component
18. James Forshaw @tiraniddo
Object Manager Reparsing
NtOpenSemaphore ObOpenObjectByName ObpLookupObjectName
18
Parsing Name
MyObjectsGlobalMySema
Current Component
19. James Forshaw @tiraniddo
Object Manager Reparsing
NtOpenSemaphore ObOpenObjectByName ObpLookupObjectName
ObpParseSymbolicLink
19
Parsing Name
MyObjectsGlobalMySema
Current Component
Global → BaseNamedObjects
20. James Forshaw @tiraniddo
Object Manager Reparsing
NtOpenSemaphore ObOpenObjectByName ObpLookupObjectName
ObpParseSymbolicLink
20
Parsing Name
MyObjectsGlobalMySema
BaseNamedObjectsMySema
Global → BaseNamedObjects
21. James Forshaw @tiraniddo
Object Manager Reparsing
NtOpenSemaphore ObOpenObjectByName ObpLookupObjectName
ObpParseSymbolicLink
STATUS_REPARSE
21
Parsing Name
BaseNamedObjectsMySema
22. James Forshaw @tiraniddo
Abusing Object Manager Symbolic Links
● Most obvious attack is object squatting
○ Redirect privileged object creation to another name
○ Open named pipes for attacking impersonation
○ Shadowing ALPC ports
● File symlink attacks perhaps more interesting!
22
35. James Forshaw @tiraniddo
Under the hood
35
NtOpenKey ObOpenObjectByName ObpLookupObjectName
Parsing Name
RegistryMachineMylink
36. James Forshaw @tiraniddo
Under the hood
36
NtOpenKey ObOpenObjectByName ObpLookupObjectName
CmpParseKey
Parsing Name
RegistryMachineMylink CmpGetSymbolicLink
Current Component
37. James Forshaw @tiraniddo
Under the hood
37
NtOpenKey ObOpenObjectByName ObpLookupObjectName
CmpParseKeySTATUS_REPARSE
CmpGetSymbolicLink
Parsing Name
RegistryMachineMylink
RegistryMachineNewKey
38. James Forshaw @tiraniddo
Serious Limitations
● Windows 7 fixed numerous issues with registry symbolic links
○ Blocked symlinks between untrusted (user) and trusted (local
machine) hives
○ Symbolic link must be a valid registry path
● MS10-021 ensured it was also available downstream
● Still can exploit user to user vulnerabilities such as in IE EPM
○ CVE-2013-5054
○ CVE-2014-6322
● Mitigation (pass flag to RegCreateKeyEx) still undocumented
38
40. James Forshaw @tiraniddo
Under the hood
40
NtOpenFile ObOpenObjectByName ObpLookupObjectName
IopParseDevice
Parsing Name
??C:tempmylinkfile NTFS Driver
41. James Forshaw @tiraniddo
Under the hood
41
NtOpenFile ObOpenObjectByName ObpLookupObjectName
IopParseDevice
Parsing Name
??C:tempmylinkfile
??C:Windows
NTFS Driver
42. James Forshaw @tiraniddo
Under the hood
42
NtOpenFile ObOpenObjectByName ObpLookupObjectName
IopParseDevice
NTFS Driver
STATUS_REPARSE
Parsing Name
??C:Windowsfile
43. James Forshaw @tiraniddo
Structure of a Mount Point
typedef struct MOUNT_POINT_REPARSE_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
};
43
Set to 0xA0000003 for Mount Point
Substitute NT Name
Print Name?
String Data
Header
Reparse
Data
44. James Forshaw @tiraniddo
Create a Mount Point
PREPARSE_DATA_BUFFER reparse_buffer =
BuildMountPoint(target);
CreateDirectory(dir);
HANDLE handle = CreateFile(dir, ...,
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OPEN_REPARSE_POINT, ...);
DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT,
reparse_buffer, reparse_buffer.size(), ...);
44
45. James Forshaw @tiraniddo
Mount Point Limitations
● Directory must be empty to set the reparse data
● Target device must be an IO device (no opening registry keys for
example)
● Target device heavily restricted in IopParseDevice:
45
IO_PARSE_CONTEXT *ctx;
if (ctx->LastReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
switch(TargetDeviceType) {
case FILE_DEVICE_DISK:
case FILE_DEVICE_CD_ROM:
case FILE_DEVICE_VIRTUAL_DISK:
case FILE_DEVICE_TAPE:
break;
default:
return STATUS_IO_REPARSE_DATA_INVALID;
}
}
Limited
Device Subset
50. James Forshaw @tiraniddo
Is that an OPLOCK in your Pocket?
void SetOplock(HANDLE hFile) {
REQUEST_OPLOCK_INPUT_BUFFER inputBuffer;
REQUEST_OPLOCK_OUTPUT_BUFFER outputBuffer;
OVERLAPPED overlapped;
overlapped.hEvent = CreateEvent(...);
DeviceIoControl(hFile, FSCTL_REQUEST_OPLOCK,
&inputBuffer, sizeof(inputBuffer),
&outputBuffer, sizeof(outputBuffer),
nullptr, &overlapped);
WaitForSingleObject(overlapped.hEvent, ...);
}
50
51. James Forshaw @tiraniddo
User Application
Exploitation
Task Scheduler Service
51
IRegisteredTask::Run
Current Mount Point:
MyTaskFolder → C:dummy
Open File for Reading
C:DummyMyTask
52. James Forshaw @tiraniddo
User Application
Exploitation
Task Scheduler Service
52
IRegisteredTask::Run
Current Mount Point:
MyTaskFolder → C:windows
Open File for Reading
C:DummyMyTask
Change Mount Point
Location
Event Set
53. James Forshaw @tiraniddo
User Application
Exploitation
Task Scheduler Service
53
IRegisteredTask::Run
Current Mount Point:
MyTaskFolder → C:windows
Open File for Reading
C:DummyMyTask
Release Oplock
Change Mount Point
Location
Event Set
54. James Forshaw @tiraniddo
User Application
Exploitation
Task Scheduler Service
54
IRegisteredTask::Run
Current Mount Point:
MyTaskFolder → C:windows
Open File for Reading
C:DummyMyTask
Generate and Verify
Hash of File
C:DummyMyTask
Release Oplock
Change Mount Point
Location
Event Set
55. James Forshaw @tiraniddo
User Application
Exploitation
Task Scheduler Service
55
IRegisteredTask::Run
Current Mount Point:
MyTaskFolder → C:windows
Open File for Reading
C:DummyMyTask
Rewrite Task File
C:WindowsMyTask
Generate and Verify
Hash of File
C:DummyMyTask
Release Oplock
Change Mount Point
Location
Event Set
56. James Forshaw @tiraniddo
OPLOCK Limitations
● Can’t block on access to standard attributes or
FILE_READ_ATTRIBUTES
● One-shot, need to be quick to reestablish if opened multiple times
● Can get around attribute reading in certain circumstances by
oplocking a directory.
● For example these scenarios opens directories for read access
○ Shell SHParseDisplayName accesses each directory in path
○ GetLongPathName or GetShortPathName
○ FindFirstFile/FindNextFile
56
64. James Forshaw @tiraniddo
Hypothetical Scenario
NTSTATUS Handle_OpenLog(PIRP Irp) {
OBJECT_ATTRIBUTES objattr;
UNICODE_STRING name;
RtlInitUnicodeString(&name,
L"SystemRootLogFilesuser.log");
InitObjectAttributes(&objattr, &name, 0, 0, 0, 0);
PHANDLE Handle = Irp->AssociatedIrp->SystemBuffer;
return ZwCreateFile(Handle, &objattr, ...);
}
64
Returns handle to user
mode process
65. James Forshaw @tiraniddo 65
SMBv2 Symbolic Links
https://msdn.microsoft.com/en-us/library/cc246542.aspx
66. James Forshaw @tiraniddo
SMBv2 Symbolic Link Restrictions
66
● Remote to Local
would be useful
● Disabled by default
in local security
policy
67. James Forshaw @tiraniddo
Back to IopParseDevice
enum SymlinkDeviceType { Local, Network };
if (ctx->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { // ... }
else {
SymlinkDeviceType target_type =
GetSymlinkDeviceType(TargetDeviceType);
if (target_type == Local || target_type == Network)
{
if (!NT_SUCCESS(IopSymlinkEnforceEnabledTypes(
target_type, ctx->last_target_type))) {
return STATUS_IO_REPARSE_DATA_INVALID;
}
}
}
67
Enforces Symlink
Traversal based on
device types
68. James Forshaw @tiraniddo
MRXSMB20
NTSTATUS Smb2Create_Finalize(SMB_CONTEXT* ctx) {
// Make request and get response
if (RequestResult == STATUS_STOPPED_ON_SYMLINK) {
result = FsRtlValidateReparsePointBuffer(
ctx->ErrorData, ctx->ErrorDataLength);
if (!NT_SUCCESS(result)) {
return result;
}
}
// ...
}
68
No check on
ReparseTag
69. James Forshaw @tiraniddo
SMBv2 Device Type Bypass
69
NtOpenFile ObpLookupObjectName
IopParseDevice
SMB2 Driver
Parsing Name
serversharefile
Current Component
Server
Create sharefile
70. James Forshaw @tiraniddo
SMBv2 Device Type Bypass
70
NtOpenFile ObpLookupObjectName
IopParseDevice
SMB2 Driver
STATUS_REPARSE
Parsing Name
serversharefile
Current Component
Server
STATUS_STOPPED_ON_SYMLINK
with
IO_REPARSE_TAG_MOUNT_POINT
71. James Forshaw @tiraniddo
SMBv2 Device Type Bypass
71
NtOpenFile ObpLookupObjectName
IopParseDevice
SMB2 Driver
Parsing Name
serversharefile
??C:hello.txt
Server
NTFS Driver
76. James Forshaw @tiraniddo
The NtCreateFile Paradox
FILE_DIRECTORY_FILE Flag
FILE_NON_DIRECTORY_FILE Flag
76
Neither FILE_DIRECTORY_FILE or FILE_NON_DIRECTORY_FILE
77. James Forshaw @tiraniddo
The Old ADS Directory Trick
Using $INDEX_ALLOCATION stream
will bypass initial directory failure
77
79. James Forshaw @tiraniddo
Let Our Powers Combine
79
NtOpenFile ObpLookupObjectName
IopParseDevice
Parsing Name
??C:tempmylink
RPC Controlmylink
NTFS Driver
STATUS_REPARSE
80. James Forshaw @tiraniddo 80
NtOpenFile ObpLookupObjectName
IopParseDevice
NTFS Driver
ObpParseSymbolicLink
STATUS_REPARSE
Parsing Name
RPC Controlmylink
??C:hello.txt
Let Our Powers Combine
81. James Forshaw @tiraniddo
Persisting the Symlink
● Might be useful to persist the symlink between login sessions
● Can’t pass OBJ_PERMANENT directly
○ Needs SeCreatePermanentPrivilege
● Get CSRSS to do it for us :-)
81
DefineDosDeviceW(
DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH,
L"GLOBALROOTRPC Controlmylink",
L"TargetPath"
);
82. James Forshaw @tiraniddo
Combined Symbolic Link Limitations
● All existing limitations of Mount Points apply
● Vulnerable application can’t try to list or inspect the mount point
itself
○ Listing the directory
○ Open for GetFileAttributes or similar
● Can mitigate somewhat by clever tricks with oplocks on directory
hierarchy
82
96. James Forshaw @tiraniddo
Finding an Ideal Service
96
Requirement Spooler Service
Runs as NT AUTHORITYSYSTEM Yup
Uses impersonation Definitely
Accessible by normal user Kind of the point
Has a habit of loading DLLs Think of all the printer drivers