Server-Side Request Forgery in .NET
Vulnerable Example
In this example, the /avatar
API is used to update the user’s avatar with a user-supplied URL. The reasoning is that the JavaScript web front end will always fill in the field, choosing among some possibilities (such as gravatar, tumblr, twitter, google).
https://www.vulnerableapp.com/avatar?url=https%253A%252F%252Fapi.twitter.com%252F1.1%252Fusers%252Fshow.json%253Fscreen_name%253Dusername
The value of the url
parameter is the following Twitter API value: https://api.twitter.com/1.1/users/show.json?screen_name=username
url-encoded two times; this accounts for the first URL-decode, which will take place inside our web server, and the second URL-decode, which will take place in Twitter’s server. Here’s the piece of code that retrieves the external URL.
WebRequest request = WebRequest.Create(inputURL);
WebResponse response = request.GetResponse();
The code is vulnerable as it trusts the attacker input of the full URL; an attack vector such as the following url=file:///etc/passwd
will return the content of the file to the attacker (in Windows, the equivalent syntax is url=file:///C:/path/to/file
)
Prevention
If the list of permitted URLs is known, implement an allow list. In the same example above, the new API will be invoked in this way:
http://vulnerableapp.com/api/avatar/update?external=true&url=1&user=screen_name%3Dusername
In the above URL, the user parameter contains both the name and the value of the parameters to be sent to the Twitter API; the %3D
is the URL-encoded version of the =
character. If the actual =
character has to be sent to the third party URL, it must be encoded twice inside the value of user
.
enum AvatarURL
{
Twitter = 0,
Gravatar = 1,
Google = 2,
Facebook = 3
}
Dictionary allowedURLsForAvatar = new Dictionary();
allowedURLsForAvatar.Add(AvatarURL.Twitter,"https://api.twitter.com/1.1/users/show.json");
AvatarURL avatarURLEnum;
Enum.TryParse("0", out avatarURLEnum);
string safeURL = "";
allowedURLsForAvatar.TryGetValue(avatarURLEnum, out safeURL);
if (safeURL == null){
// Return an error message
} else {
...
// build and use the final URL
string finalURL=safeURL;
if (user != null) {
// account for encoding
string paramName = System.Web.HttpUtility.UrlEncode(user.Split("=").First());
string paramValue = System.Web.HttpUtility.UrlEncode(user.Split("=").Last());
finalURL += '?' + paramName + '=' + paramValue;
}
...
}
References
OWASP - Server Side Request Forgery Acunetix - What is Server Side Request Forgery Dzone: The Server Side Request Forgery Vulnerability and How to Prevent It