Unrestricted File Download in .NET
Vulnerable Example
The snippet below is a helper function used to concatenate the directory that contains the asset files, /opt/wwwdata/assets/
with the file name passed as assetName
.
using System;
public class Assets
{
public static string assetsDir = "/opt/wwwdata/assets/";
public static string getAssetPath(string assetName)
{
return System.IO.Path.Combine(
assetsDir,
assetName
);
}
}
If assetName
is controlled by the user, it is possible to conduct a path traversal attack and escape the intended directory, for example by using ../../../../../../etc/passwd
as file asset name.
This results in the absolute path /opt/wwwdata/assets/../../../../../etc/passwd
which is canonicalized by the operating system as /etc/passwd
.
Prevention
To validate that a path does not point to an unintended location, use Path.GetFullPath()
to get the absolute path, and then validate it starts as expected.
public static string getAssetPath(string assetName)
{
string assetPath = System.IO.Path.Combine(
assetsDir,
assetName
);
if(!Path.GetFullPath(assetPath).StartsWith(assetsDir)) {
throw new System.InvalidOperationException("Provided asset name is invalid");
}
return assetPath;
}
To sanitize the path, it is possible to use System.IO.Path.GetFileName()
to concatenate only the final part of the user-provided file name.
public static string getAssetPath(string assetName)
{
return System.IO.Path.Combine(
assetsDir,
System.IO.Path.GetFileName(assetName)
);
}
The previous attack would result in reading the /opt/wwwdata/assets/passwd
file.