I ran into strange behavior of PowerShell, which I consider being a security issue. I don’t know if this is expected behavior built in by Microsoft, but by using this “bug”, you can check the contents of a folder to which you don’t have access permissions. When using the Test-Path cmdlet, the command behaves a bit different than expected.
If Test-Path is used on a file which is in a folder where you don’t have access permissions to (eg. NTFS permissions are set), the command returns $false, but also throws an UnauthorizedAccessException (access denied). However, when the file actually doesn’t exist, the command will only return $false. By catching the error, you know if the file exists or not. By using a brute-force method, you can enumerate the entire contents of the directory. This way it’s possible to find out what kind of applications are installed on a machine, and possibly exploiting weakness in these applications.
Reproducing the issue
Default behavior of the Test-Path cmdlet is to return either $true or $false:
PS C:\> Test-Path C:\DoesNotExist\file.txt
False
I created a directory called “C:\Data\Temp” and added some subfolders and files. Next I removed access to the directory by modifying NTFS permissions. Checking the C:\Data\Temp path works as expected:
PS C:\> Test-Path C:\Data\Temp
True
Next I checked a file inside the directory to which I don’t have access:
PS C:\> Test-Path C:\Data\Temp\test.txt
Test-Path : Access is denied
At line:1 char:1
+ Test-Path C:\Data\Temp\test.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Data\Temp\test.txt:String) [Test-Path], UnauthorizedAccessExceptio
n
+ FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
False
The command returns an UnauthorizedAccessException, correctly reporting that I don’t have access to the folder. But when I check a file inside the directory which does not exist:
PS C:\> Test-Path C:\Data\Temp\doesnotexist.txt
False
The command just returns $false. The command should return an UnauthorizedAccessException as well in my opinion, since I don’t have access to the entire directory. Since the Test-Path command returns an error when the file exists, you can catch the error and report that the file actually does exist.
Exploiting the issue
I wrote a PowerShell function which exploits this behavior:
Function Test-File { Param ( [Parameter(Mandatory=$true,Position=0)][string]$Path ) try { $return = Test-Path -Path $Path -ErrorAction Stop } catch [System.UnauthorizedAccessException] { return $true } return $return }
When you run the Test-File command on a file that does exist, but to which you don’t have access, the script will return $true:
PS C:\> Test-File -Path C:\Data\Temp\test.txt
True
This script can even be extended to allow a brute-force check of files in directories. This way you can enumerate the contents of a directory, even without access to it.
Limitation
This behavior is limited to the local system only. Using Test-Path on a remote system, over UNC path for example, will always return the UnauthorizedAccessException exception, even if the file doesn’t exist.
PS C:\> Test-Path \\SERVERNAME\C$\MyFile.txt
Test-Path : Access is denied
At line:1 char:1
+ Test-Path \\SERVERNAME\C$\MyFile.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (\\SERVERNAME\C$\MyFile.txt:String) [Test-Path], UnauthorizedAccessE
xception
+ FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
False
This limitation is a good thing, since it means that you can only exploit the behavior when you already have access to a machine.
I hope this article was useful for you. If you have any questions, please don’t hesitate to leave a comment or contact me over email. |