Note: This is the summary of my posts (they are written in Japanese):
I already reported this as a bug for Xamarin’s Bugzilla.
Introduction
I found the different behavior of WebClient.DownloadString(String)
between Mono and .NET Framework when an invalid URI passed to it. In the Mono’s implementation, it may cause a security issue.
This causes by two different behaviors, in new Uri(String)
, and Path.GetFullPath(String)
.
DownloadString
DownloadString(String)
and some methods (e.g. DownloadFile(String)
, OpenRead(String)
, etc.) calls CreateUri(String)
, a private method of WebClient
. (-> source code on github)
CreateUri and GetUri
CreateUri(String)
tries to make an instance of Uri
with new Uri(String)
. If an invalid URI passed, the constructor raises an exception. For example, new Uri("http://../../../etc/passwd")
will be failed because its hostname part (..
) is invalid. However, the failure will be ignored, and CreateUri(String)
returns new Uri(Path.GetFullPath(String))
. It means the local file address with the full path will be returned.
In .NET Framework, DownloadString(String)
calls GetUri(String)
, and it may have the same behavior as CreateUri(String)
. (I didn’t read its implementation, but I guess it from the stack trace when an exception is thrown.)
Uri constructor
In .NET, new Uri(String)
fails when an invalid URI with a known scheme is given (e.g. http://../../etc/passwd
), however, it succeeds when a invalid URI with an unknown scheme is given (e.g. abc://../../etc/passwd
).
In Mono, new Uri(String)
fails for both cases.
This is the first different behavior.
Path.GetFullPath
While Path.GetFullPath
fails for URIs which has known schemes in .NET, it succeeds for any URIs in Mono.
For example, http://../etc/passwd
is given, .NET’s Path.GetFullPath
fails with ArgumentException (“URI formats are not supported.”), but Mono’s succeeds and returns the full path like /home/mayth/etc/passwd
.
Another example: abc://../etc/passwd
is given, .NET’s Path.GetFullPath
will succeeds, and Mono’s also succeeds.
This result shows that .NET’s implemantation may recognize the scheme of the given URI, and Mono’s one may not.
This is the second different behavior.
The Flow of DownloadString(String)
.NET
Invalid URIs with known scheme
By default, ‘known scheme’ means these schemes:
- http://
- https://
- ftp://
- file://
This is documented on MSDN (See: WebRequest.Create Method (String)).
parameter: addr = http://../../etc/passwd
GetUri(addr)
is called- Try to
new Uri(addr)
, and it fails - An exception is ignored, and
Path.GetFullPath(addr)
is called Path.GetFullPath(addr)
throws ArgumentException with the message: “URI formats are not supported.”
Invalid URIs with unknown scheme
parameter: addr = abc://../../etc/passwd
GetUri(addr)
is called- Try to
new Uri(addr)
, and it succeeds. GetUri(addr)
returns the result of step2.WebRequest.Create
is called.- It throws NotSupportedException because it does not know how to handle the given URI’s scheme.
Mono
In Mono, there is no difference whether the URI’s scheme is known or not.
parameter: addr = http://../../etc/passwd
GetUri(addr)
is called- Try to
new Uri(addr)
, and it fails - An exception is ignored, and
Path.GetFullPath(addr)
is called Path.GetFullPath(addr)
returns the full path (e.g./home/etc/passwd
)- The full path passed to
new Uri(String)
. This may resultfile:///home/etc/passwd
. - Attempt to acquire the resource,
file:///home/etc/passwd
.
Security Issue
Like the samples shown above, DownloadString(String)
can access to the local resource, like /etc/passwd
.
Directory Traversal
If the program runs at /home/mayth, for example, the code below will returns the content of /etc/passwd
.
WebClient.DownloadString("http://../../../etc/passwd")
Of course, any files that is readable from the user who runs the program can be read. For example, the files in the home directory can be read.
The attackers can’t know the listings of the directory, but they suggest it, or guess from .bash_history
file.
Full Path Disclosure
If the program runs at /home/mayth
, the code below will fail because it won’t be found.
WebClient.DownloadString("http://../../etc/passwd")
In this case, an exception’s message is: ‘Could not find a part of the path “/home/etc/passwd”’. If you output this message somewhere (stdout, log file, etc.), the attackers may be possible to see the full path.