3

I have a problem related to file management done by a service application. My problem is that users are able to move files to the recycle bin for which I've created a hardlink, and once they do this, I loose the ability to list the hardlinks available.

This appears to only happen when the removed hardlink file sits inside the $RECYCLER folder but not on a folder with similar permissions on the same disk.

To replicate my problem assume one has a user account named Service with a suitable password.

On the current user account:

md C:\tmp
echo CONTENTS>C:\tmp\1
fsutil hardlink create C:\tmp\2 C:\tmp\1

That would have created a file named C:\tmp\1 and a hardlink to it named C:\tmp\2.

Now if you runas another terminal with user Service you can:

fsutil hardlink list C:\tmp\1
\tmp\1
\tmp\2

That works fine.

Now if you (as the original user) move the 2 file to the recycle bin, you cannot access the files as Service.

type C:\tmp\1
Access is denied.
fsutil hardlink list C:\tmp\1
Error:  Access is denied.

That's because Explorer will have changed the DACL on the file to a restrictive one where only the current user, SYSTEM and an Administrator have access to the file.

If you do a icacls C:\tmp\1 /reset as the original user, now you can access the contents of the file as Service:

type C:\tmp\1
CONTENTS

But if you try to list the hardlinks, it will show you the first link and an access denied error:

fsutil hardlink list C:\tmp\1
\tmp\1
Error:  Access is denied.

If you list the hardlinks on the file as the original user, you get to know the recycle bin path of the original file:

fsutil hardlink list C:\tmp\1
\tmp\1
\$Recycle.Bin\S-1-5-21-111111111-2222222222-3333333333-1002\$R1GX1HN

And if you move that file to another (as the original user) folder:

md C:\tmp2
move \$Recycle.Bin\S-1-5-21-111111111-2222222222-3333333333-1002\$R1GX1HN C:\tmp2

Now as Service you can list all the hardlinks:

fsutil hardlink list C:\tmp\1
\tmp\1
\tmp2\$R1GX1HN

Any idea why this may be happening?

Does this have anything to do with Mandatory Integrity Control?

Pablo Montilla
  • 2,941
  • 1
  • 31
  • 35
  • The service has no access to the user's recycle bin folder. It should have SeChangeNotifyPrivilege to bypass traverse checking, so it should be able to access the file itself at a low level (e.g. via `CreateFileW` or C `_wopen`), but at a higher level a lot of programs need access to the parent directory, e.g. to be able to stat the file via `FindFirstFileW`. – Eryk Sun Aug 16 '19 at 14:35
  • So `FindFirstFileW` requires permission to access the parent folder? – Pablo Montilla Aug 16 '19 at 15:27
  • Sorry, I should have said `FindFirstFileNameW` which along with `FindNextFileNameW` are the functions used to get hardlinks associated with a file. You think that still requires reading of the parent directory? – Pablo Montilla Aug 16 '19 at 16:30
  • That can be, but I'm having the problem using the Win32 API...the example was done with the command line so it is easily reproducible. – Pablo Montilla Aug 16 '19 at 16:35
  • 1
    `FindFirstFileNameW` opens the file and calls `NtQueryInformationFile` : `FileHardLinkInformation`. This returns [`FILE_LINK_ENTRY_INFORMATION`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/ns-ntifs-_file_link_entry_information) records. Notice that it's not the full path of the link on the volume. It's the link base filename and the ID of the parent directory. So `FindFirstFileNameW` or `FindNextFileNameW` has to open the parent directory by ID in order to query its path. In the case of the link in the recycle bin, it fails with access denied. – Eryk Sun Aug 16 '19 at 16:53
  • Excellent. That explains it. Do you think I can fix that problem by adding the `SeChangeNotifyPrivilege` to the service user? Also, can you put that as an answer so I can give you due credit? – Pablo Montilla Aug 16 '19 at 16:58
  • The access it needs on the link's parent directory is `SYNCHRONIZE | FILE_READ_ATTRIBUTES`. – Eryk Sun Aug 16 '19 at 16:58
  • 1
    SeChangeNotifyPrivilege allows *traversing* a directory regardless of your access. It doesn't allow actually getting access to a directory that you're not granted. That you can get via SeBackupPrivilege -- but only if the `NtCreateFile` or `NtOpenFile` call actually requests backup semantics. Let me check this case. – Eryk Sun Aug 16 '19 at 17:00
  • 1
    Nope. The `NtCreateFile` call uses the options `FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT`. It doesn't include the option `FILE_OPEN_FOR_BACKUP_INTENT`, so enabling SeBackupPrivilege won't help. – Eryk Sun Aug 16 '19 at 17:03

1 Answers1

0

As explained in the comments by @Eryk Sun, the problem is related to the way hardlinks are reported by Win32 API.

It appears that fsutil uses FindFirstFileName and FindNextFileName functions, both of which use NtQueryInformationFile which in turn returns a structure with entries of type FILE_LINK_ENTRY_INFORMATION which have the hardlink name and the parent folder NTFS ID.

The problem manifests itself when the caller does not have permission to open the containing folder to get its name (probably using OpenFileById and GetFinalPathByHandle).

Pablo Montilla
  • 2,941
  • 1
  • 31
  • 35