404 File not found due to FileUtil.CheckSuspiciousPhysicalPath
As everyone of you knows, 404 means file not found! A complete reference is available here: https://support.microsoft.com/kb/943891/en-gb. Well, there is a scenario where IIS provides back an HTTP 404 even if your file is there! Unbelievable.. Let us have a look to it. First of all, proceed configuring web server to provide back this code, after that let’s see where the issue is! Define a web site on IIS and configure its path on a system driver, for example E:. After that, define a web application inside it. Store web application files inside the same drive folder. For example E:\TestMicrosoft. This is my configuration:
As you can see, I added a txt resource and an ASP.NET page. If you navigate to web: https://localhost/TestCarmelop/example.aspx IIS answers with an HTTP 404. This behavior does not occurs for a resource not handled by ASP.NET ISAPI https://localhost/TestCarmelop/test.txt. Why? A good way to understand it is by enabling the Failed Request Tracing Rules: https://www.iis.net/learn/troubleshoot/using-failed-request-tracing/troubleshooting-failed-requests-using-tracing-in-iis
Reading the trace, we can find out the following information:
It seems that during the request, processing and exception occurred. Let us have a look to .NET source code ( you can use reflector on ILSPY for example). In my case, application is running on top of .Net Framework 2.0, so I will refer these assemblies. Please have a look at code mentioned below.
As you can see from above call stack, ASP.NET ISAPI invokes method: System.Web.CachedPathData.GetConfigPathData. This is done to get the upper level hierarchy settings. From this method, we jump to FileUtil.CheckSuspiciousPhysicalPath and there our path is considered suspect by ASP.NET runtime. So an exception is thrown: throw new HttpException(404, string.Empty); It is exactly our HTTP status code! The question is: Why our path is considered suspected? If we collect a memory dump, we can see that the value of path analyzed is E:\. This path is considered suspect in consideration of fact that you can access to every location of the entire drive. The solution, is to move the web site definition in a driver folder, something like E:\Mysite.
Enjoy your debug
Carmelo
// System.Web.CachedPathData
private static CachedPathData GetConfigPathData(string configPath)
{
string key = CachedPathData.CreateKey(configPath);
CacheInternal cacheInternal = HttpRuntime.CacheInternal;
CachedPathData cachedPathData = (CachedPathData)cacheInternal.Get(key);
if (cachedPathData != null)
{
cachedPathData.WaitForInit();
return cachedPathData;
}
CachedPathData parentData = null;
CacheDependency cacheDependency = null;
VirtualPath virtualPath = null;
string text = null;
bool flag = false;
string[] filenames = null;
string[] cachekeys = null;
string text2 = null;
bool flag2 = false;
if (WebConfigurationHost.IsMachineConfigPath(configPath))
{
flag = true;
flag2 = true;
}
else
{
string parent = ConfigPathUtility.GetParent(configPath);
parentData = CachedPathData.GetConfigPathData(parent);
string text3 = CachedPathData.CreateKey(parent);
cachekeys = new string[]
{
text3
};
if (!WebConfigurationHost.IsVirtualPathConfigPath(configPath))
{
flag = true;
flag2 = true;
}
else
{
WebConfigurationHost.GetSiteIDAndVPathFromConfigPath(configPath, out text2, out virtualPath);
try
{
text = virtualPath.MapPathInternal(true);
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == 500)
{
throw new HttpException(404, string.Empty);
}
throw;
}
FileUtil.CheckSuspiciousPhysicalPath(text);
bool flag3 = false;
if (string.IsNullOrEmpty(text))
{
flag = false;
}
else
{
FileUtil.PhysicalPathStatus(text, false, false, out flag, out flag3);
}
if (flag && !flag3)
{
....
// System.Web.Util.FileUtil
internal static void CheckSuspiciousPhysicalPath(string physicalPath)
{
if (FileUtil.IsSuspiciousPhysicalPath(physicalPath))
{
throw new HttpException(404, string.Empty);
}
}