OWASP Testing Guide v3

Every vibrant technology marketplace needs an unbiased source of information on best practices as well as an active body advocating open standards. In the Application Security space, one of those groups is the Open Web Application Security Project (or OWASP for short).

The Open Web Application Security Project (OWASP) is a 501(c)(3) worldwide not-for-profit charitable organization focused on improving the security of software. Our mission is to make software security visible, so that individuals and organizations are able to make informed decisions. OWASP is in a unique position to provide impartial, practical information about AppSec to individuals, corporations, universities, government agencies and other organizations worldwide. Operating as a community of like-minded professionals, OWASP issues software tools and knowledge-based documentation on application security.

The entire Open Web Application Security Project (OWASP) Testing Guide v3 can be downloaded here

Testing for Vulnerable Remember Password and Password Reset

Brief Summary

Most web applications allow users to reset their password if they have forgotten it, usually by sending them a password reset email and/or by asking them to answer one or more “security questions.” In this test, we check that this function is properly implemented and that it does not introduce any flaw in the authentication scheme. We also check whether the application allows the user to store the password in the browser (“remember password” function).

Description of the Issue

A great majority of web applications provide a way for users to recover (or reset) their password in case they have forgotten it. The exact procedure varies heavily among different applications, also depending on the required level of security, but the approach is always to use an alternate way of verifying the identity of the user. One of the simplest (and most common) approaches is to have on file the user’s email address (e.g., this is obtained when the user first registers), and send the old password (or a new one) to that address. This scheme is based on the assumption that the user’s email has not been compromised and that is secure enough for this goal.
Alternatively (or in addition to that), the application could ask the user to answer one or more “secret questions”, which are usually chosen by the user among a set of possible ones. The security of this scheme lies in the ability to provide a way for someone to identify themselves to the system with answers to questions that are not easily answerable via personal information lookups. As an example, a very insecure question would be “your mother’s maiden name” since that is a piece of information that an attacker could find out without much effort. An example of a better question would be “favorite grade-school teacher” since this would be a much more difficult topic to research about a person whose identity may otherwise already be stolen.
Another common feature that applications use to provide users a convenience is to cache the password locally in the browser (on the client machine) and having it ‘pre-typed’ in all subsequent accesses. While this feature can be perceived as extremely friendly for the average user, at the same time, it introduces a flaw, as the user account becomes easily accessible to anyone that uses the same account on the client machine.

Black Box Testing and Examples

Password Reset
The first step is to check whether secret questions are used. Sending the password (or a password reset link) to the user email address without first asking for a secret question means relying 100% on the security of that email address, which is not suitable if the application needs a high level of security.
On the other hand, if secret questions are used, the next step is to assess their strength.
As a first point, how many questions need to be answered before the password can be reset? The majority of applications only need the user to answer to one question, but some critical applications require the user to answer correctly to two or even more questions.
As a second step, we need to analyze the questions themselves. Often a self-reset system offers the choice of multiple questions; this is a good sign for the would-be attacker as this presents him/her with options. Ask yourself whether you could obtain answers to any or all of these questions via a simple Google search on the Internet or with a social engineering attack. As a penetration tester, here is a step-by-step walk-through of assessing a password self-reset tool:

  • Are there multiple questions offered?
    • If so, try to pick a question which would have a “public” answer; for example, something Google would find with a simple query
    • Always pick questions which have a factual answer such as a “first school” or other facts which can be looked up
    • Look for questions which have few possible options, such as “what make was your first car”. These questions would present the attacker with a short-list of answers to guess at and based on statistics the attacker could rank answers from most to least likely
  • Determine how many guesses you have (if possible)
    • Does the password reset allow unlimited attempts?
    • Is there a lockout period after X incorrect answers? Keep in mind that a lockout system can be a security problem in itself, as it can be exploited by an attacker to launch a Denial of Service against legitimate users
  • Pick the appropriate question based on analysis from above point, and do research to determine the most likely answers
  • How does the password-reset tool (once a successful answer to a question is found) behave?
    • Does it allow immediate change of the password?
    • Does it display the old password?
    • Does it email the password to some pre-defined email address?
    • The most insecure scenario here is if the password reset tool shows you the password; this gives the attacker the ability to log into the account, and unless the application provides information about the last login the victim would not know that his/her account has been compromised.
    • A less insecure scenario is if the password reset tool forces the user to immediately change his/her password. While not as stealthy as the first case, it allows the attacker to gain access and locks the real user out.
    • The best security is achieved if the password reset is done via an email to the address the user initially registered with, or some other email address; this forces the attacker to not only guess at which email account the password reset was sent to (unless the application tells that) but also to compromise that account in order to take control of the victim’s access to the application.

The key to successfully exploiting and bypassing a password self-reset is to find a question or set of questions which give the possibility of easily acquiring the answers. Always look for questions which can give you the greatest statistical chance of guessing the correct answer, if you are completely unsure of any of the answers. In the end, a password self-reset tool is only as strong as the weakest question. As a side note, if the application sends/visualizes the old password in cleartext it means that passwords are not stored in a hashed form, which is a security issue in itself.

Password Remember

The “remember my password” mechanism can be implemented with one of the following methods:

  1. Allowing the “cache password” feature in web browsers. As of 2014 this is the preferred method as all major browsers have disabled the setting of autocomplete=”off” by default for password fields.
  2. Storing the password in a permanent cookie. The password must be hashed/encrypted and not sent in the clear.

To check the second implementation type, examine the cookies stored by the application. Verify that the credentials are not stored in cleartext, but are hashed. Examine the hashing mechanism: if it is a common, well-known algorithm, check for its strength; in homegrown hash functions, attempt several usernames to check whether the hash function is easily guessable. Additionally, verify that the credentials are only sent during the login phase, and not sent together with every request to the application.

Gray Box Testing and Examples

This test uses only functional features of the application and HTML code that is always available to the client, the graybox testing follows the same guidelines of the previous section. The only exception is for the password encoded in the cookie, where the same gray box analysis described in the Testing for Session Management Schema chapter can be applied.

Original Article

Vertical And Horizontal Privilege Escalation

What is Vertical And Horizontal Privilege Escalation?  Does your application support for this internally authorisation verification of work?  I expect not, it is really an over sight of the architecture and application planning that this is missed out and more importantly when it becomes an issue it is more often than not too late to implement any changes.

So what is Vertical And Horizontal Privilege Escalation?  

Privilege escalation is the act of exploiting a bug, design flaw or configuration oversight in an operating system or software application to gain elevated access to resources that are normally protected from an application or user. The result is that an application with more privileges than intended by the application developer or system administrator can perform unauthorized actions.

You may find that you have alternative methods to protect against privilege escalation:

  • Authorize attribute: This is used to authorize a user or role to access any resource in the application after authenticated. This helps to have User and Role level protections.
  • Http Referrer Check: This is to prevent an URL request which is not from the site, but from an external link or the link directly executed at the browser navigation
  • Anti Forgery Token: This is a powerful option to prevent any hidden field manipulation while form posting and prevents Cross-Site Request Forgery
  • HTML Encoding: It is advised to encode all user inputs to prevent Cross Site Scripting attack/ XSS attack etc.,
  • Encryption of Query string parameters. This is good way to prevent manipulation of query string parameters. 
  • URL Activity Tampering, we provide a key in the URL that can only be used for that URL

Even though you may not support Privilege Escalation directly you can place a front line of defence, Privilege escalation means a user receives privileges they are not entitled to. These privileges can be used to change information, view private information, or install unwanted programs such as viruses. It usually occurs when a system has a bug that allows security to be bypassed or, alternatively, has flawed design assumptions about how it will be used. Privilege escalation occurs in two forms:

  • Vertical privilege escalation, also known as privilege elevation, where a lower privilege user or application accesses functions or content reserved for higher privilege users or applications 
  • Horizontal privilege escalation, where a normal user accesses functions or content reserved for other normal users
I hope this help you understand, that planning and understanding issues from the outset is so important.

URL Activity Tampering and how to prevent it in MVC

No web applications are developed without concerning the security issues. But it is not sure whether the security measures are implemented correctly and the applications are well protected. We could see a lot of web applications query string parameters in plain string which are easy to manipulate and way to escalate restricted resources. Even the URL parameters are encrypted there is still a chance of a bruteforce attack by manipulating the encrypted hash and it yields when the database is large where the chance of matching a random hash is high. So it is clear the importance to protect the URL in the web application is necessary for all programming platforms. Here, this is discussed with reference to ASP.NET MVC and the utility class is presented.

.NET Framework has a lot of improved methods for security and the MVC framework introduced new security methods like AntiForgeryToken that I discussed in a previous article Preventing Cross Site Request Forgery. Let us have a brief look at the security methods in general practice.

  • Authorize attribute: This is useful to authorize a user or role to access any resource in the application after authenticated. This helps to have User and Role level protections.
  • Http Referrer Check: This is a general method not confined to .NET. This is to prevent an URL request which is not from the site, but from an external link or the link directly executed at the browser navigation
  • Anti Forgery Token: This is a powerful option to prevent any hidden field manipulation while form posting and prevents Cross-Site Request Forgery
  • HTML Encoding: It is advised to encode all user inputs to prevent Cross Site Scripting attack/ XSS attack etc.,
  • Protection against SQL injections
  • Encryption of Query string parameters. This is good way to prevent manipulation of query string parameters. But this is not a complete protection, still vulnerable for a brute force attack.

Even though all the above methods are available, .NET framework doesn’t provide an inbuilt functionality for protecting the URL parameters. Its URL authorization technique protects the whole URL, but not parts of the URL which are still vulnerable for manipulation. This piece of coding here helps to easily implement all these security methods with special importance to protecting the URL parameters. Creating a URL hash is not new but yet it is not a common practice in the industry though it is an important concern. So it would be important to share a piece of code for the easy implementation of this method in ASP.NET MVC.

The basic idea is simple, encrypting the URL parameters along with controller, action names to a hash and later it will be passed to server as a URL parameter. The hash will be verified at the server side for its validity by recomputing the hash and compared with the submitted hash. This hash is dynamic and specific to a session. So even though your URL cached in the man-in-the middle attack, it will not work in other sessions. Basically a utility class extends the HTML helper ActionLink class. So we can use Html.ActionLink method as such in the MVC implementation, in addition you only need to pass a boolean argument as a last argument to get the protected URL. It is pretty easy to use this coding. Examples are shown below.

<%=Html.ActionLink("link Test 3", "HandleLink", new { id = 200 }, true)%>

You’ll notice the extra boolean parameter at the end to enable encryption.

The ExtendedHtmlActionLinkHelper looks like this:

namespace ExtendedHtmlActionLinkHelper
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using System.Web.Routing;

    /// <summary>
    /// This class is an extension for the Html Helper
    /// </summary>
    public static class ActionLinkExtensions
    {
        // This password is included in the hash. If needed it can be changed.
        private static string tokenPassword = "@#123%#";

        public static string TokenPassword
        {
            get { return tokenPassword; }

            set { tokenPassword = value; }
        }

        /// <summary>
        /// This is the extended method of Html.ActionLink.This class has the extended methods for the 10 overloads of this method
        /// All the overloaded method applys the same logic excepts the parameters passed in
        /// </summary>
        /// <param name="helper">The helper.</param>
        /// <param name="linkText">The link text.</param>
        /// <param name="actionName">Name of the action.</param>
        /// <param name="generateToken">if set to <c>true</c> [generate token].</param>
        /// <returns></returns>
        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            var rvd = new RouteValueDictionary();
            if (generateToken)
            {
                // Call the generateUrlToken method which create the hash
                var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd, TokenPassword);

                // The hash is added to the route value dictionary
                rvd.Add("urltoken", token);
            }

            // the link is formed by using the GenerateLink method.
            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, null, rvd, null);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, object routeValues, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            var rvd = new RouteValueDictionary(routeValues);
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd, TokenPassword);
                rvd.Add("urltoken", token);
            }

            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, null, rvd, null);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, RouteValueDictionary routeValues, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            var rvd = routeValues;
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd, TokenPassword);
                rvd.Add("urltoken", token);
            }

            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, null, rvd, null);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            var rvd = new RouteValueDictionary();
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(controllerName, actionName, rvd, TokenPassword);
                rvd.Add("urltoken", token);
            }

            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, controllerName, rvd, null);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, object routeValues, object htmlAttributes, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            var rvd = new RouteValueDictionary(routeValues);
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd, TokenPassword);
                rvd.Add("urltoken", token);
            }

            var attrib = GetDictionaryFromObject(htmlAttributes);
            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, null, rvd, attrib);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, routeValues, TokenPassword);
                routeValues.Add("urltoken", token);
            }

            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, null, routeValues, htmlAttributes);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            var rvd = new RouteValueDictionary(routeValues);
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(controllerName, actionName, rvd, TokenPassword);
                rvd.Add("urltoken", token);
            }

            var attrib = GetDictionaryFromObject(htmlAttributes);
            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, controllerName, rvd, attrib);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(controllerName, actionName, routeValues, TokenPassword);
                routeValues.Add("urltoken", token);
            }

            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, controllerName, routeValues, htmlAttributes);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            var rvd = new RouteValueDictionary(routeValues);
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(controllerName, actionName, rvd, TokenPassword);
                rvd.Add("urltoken", token);
            }

            var attrib = GetDictionaryFromObject(htmlAttributes);
            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, controllerName, protocol, hostName, fragment, rvd, attrib);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool generateToken)
        {
            var rc = helper.ViewContext.RequestContext;
            var routeCollection = helper.RouteCollection;
            if (generateToken)
            {
                var token = TokenUtility.GenerateUrlToken(controllerName, actionName, routeValues, TokenPassword);
                routeValues.Add("urltoken", token);
            }

            var link = HtmlHelper.GenerateLink(rc, routeCollection, linkText, null, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes);
            var mvcHtmlString = MvcHtmlString.Create(link);
            return mvcHtmlString;
        }

        private static IDictionary<string, object> GetDictionaryFromObject(object sourceObject)
        {
            var objType = sourceObject.GetType();

            return objType.GetProperties().ToDictionary(prop => prop.Name, prop => prop.GetValue(sourceObject, null));
        }
    }
}

It is also worth generating the UrlHelper.Action too should this be required

using System.Web.Mvc;
using System.Web.Routing;

public static class ActionExtensions
{
    public static MvcHtmlString Action(this UrlHelper helper, string actionName, bool generateToken)
    {
        var rvd = new RouteValueDictionary();
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, rvd);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }

    public static MvcHtmlString Action(this UrlHelper helper, string actionName, object routeValues, bool generateToken)
    {
        if(routeValues== null)
        {
            routeValues = new RouteValueDictionary();
        }

        var rvd = (RouteValueDictionary)routeValues;
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, rvd);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }

    public static MvcHtmlString Action(this UrlHelper helper, string actionName, RouteValueDictionary routeValues, bool generateToken)
    {
        if (routeValues == null)
        {
            routeValues = new RouteValueDictionary();
        }

        var rvd = routeValues;
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, rvd);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }

    public static MvcHtmlString Action(this UrlHelper helper, string actionName, string controllerName, bool generateToken)
    {
        var rvd = new RouteValueDictionary();
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, controllerName, rvd);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }


    public static MvcHtmlString Action(this UrlHelper helper, string actionName, string controllerName, object routeValues, bool generateToken)
    {
        if (routeValues == null)
        {
            routeValues = new RouteValueDictionary();
        }

        var rvd = (RouteValueDictionary)routeValues;
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, controllerName, rvd);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }


    public static MvcHtmlString Action(this UrlHelper helper, string actionName, string controllerName, RouteValueDictionary routeValues, bool generateToken)
    {
        if (routeValues == null)
        {
            routeValues = new RouteValueDictionary();
        }

        var rvd = routeValues;
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, controllerName, rvd);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }


    public static MvcHtmlString Action(this UrlHelper helper, string actionName, string controllerName, object routeValues, string protocol, bool generateToken)
    {
        if (routeValues == null)
        {
            routeValues = new RouteValueDictionary();
        }

        var rvd = (RouteValueDictionary)routeValues;
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, controllerName, rvd, protocol);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }

    public static MvcHtmlString Action(this UrlHelper helper, string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName, bool generateToken)
    {
        if (routeValues == null)
        {
            routeValues = new RouteValueDictionary();
        }

        var rvd = routeValues;
        if (generateToken)
        {
            // Call the generateUrlToken method which create the hash
            var token = TokenUtility.GenerateUrlToken(string.Empty, actionName, rvd);

            // The hash is added to the route value dictionary
            rvd.Add("urltoken", token);
        }

        // the link is formed by using the GenerateLink method.
        var link = new UrlHelper(helper.RequestContext).Action(actionName, controllerName, rvd, protocol, hostName);
        var mvcHtmlString = MvcHtmlString.Create(link);
        return mvcHtmlString;
    }
        
}Getting a protected URL in the view is easy as just passing a boolean parameter. Now let us see how to validate the URL when submitted to controller. It is even simpler. Just add this attribute to the controller method.
[IsValidURLRequest]

This attribute has taken care of the simple authorize check (user name, password verification), check for http referrer and protect the URL parameters.

Now for the TokenUtility that performs most of the work for creating the URL Hash.

using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Routing;

/// <summary>
/// This is a static helper class which creates the URL Hash
/// </summary>
public static class TokenUtility
{
    public static string GenerateUrlToken(string controllerName, string actionName, RouteValueDictionary argumentParams, string password)
    {
        //// The URL hash is dynamic by assign a dynamic key in each session. So
        //// eventhough your URL is stolen, it will not work in other session
        if (HttpContext.Current.Session["url_dynamickey"] == null)
        {
            HttpContext.Current.Session["url_dynamickey"] = RandomString();
        }

        // The salt include the dynamic session key and valid for an hour.
        var salt = HttpContext.Current.Session["url_dynamickey"] + DateTime.Now.ToShortDateString() + " " + DateTime.Now.Hour;

        // generating the partial url
        var stringToToken = controllerName + "/" + actionName + "/";
        stringToToken = argumentParams.Where(item => item.Key != "controller" && item.Key != "action" && item.Key != "urltoken").Aggregate(stringToToken, (current, item) => current + (item.Value));

        // Converting the salt in to a byte array
        var saltValueBytes = System.Text.Encoding.ASCII.GetBytes(salt);

        // Encrypt the salt bytes with the password
        var key = new Rfc2898DeriveBytes(password, saltValueBytes);

        // get the key bytes from the above process
        var secretKey = key.GetBytes(16);

        // generate the hash
        var tokenHash = new HMACSHA1(secretKey);
        tokenHash.ComputeHash(System.Text.Encoding.ASCII.GetBytes(stringToToken));

        // convert the hash to a base64string
        var token = Convert.ToBase64String(tokenHash.Hash).Replace("/", "_");

        return token;
    }

    /// <summary>
    /// This validates the token
    /// </summary>
    /// <param name="token">The token.</param>
    /// <param name="controllerName">Name of the controller.</param>
    /// <param name="actionName">Name of the action.</param>
    /// <param name="argumentParams">The argument params.</param>
    /// <param name="password">The password.</param>
    /// <returns></returns>
    public static bool ValidateToken(string token, string controllerName, string actionName, RouteValueDictionary argumentParams, string password)
    {
        // compute the token for the currrent parameter
        var computedToken = GenerateUrlToken(controllerName, actionName, argumentParams, password);

        // compare with the submitted token
        if (computedToken != token)
        {
            computedToken = GenerateUrlToken(string.Empty, actionName, argumentParams, password);
        }
        else { return true; }

        return computedToken == token;
    }

    /// <summary>
    /// It validates the token, where all the parameters passed as a RouteValueDictionary
    /// </summary>
    /// <param name="requestUrlParts">The request URL parts.</param>
    /// <param name="password">The password.</param>
    /// <param name="token">the Url encrypted token to be used to validate against</param>
    /// <returns></returns>
    public static bool ValidateToken(RouteValueDictionary requestUrlParts, string password, string token)
    {
        // get the parameters
        string controllerName;
        try
        {
            controllerName = Convert.ToString(requestUrlParts["controller"]);
        }
        catch (Exception)
        {
            controllerName = string.Empty;
        }

        var actionName = Convert.ToString(requestUrlParts["action"]);
        //var token = Convert.ToString(requestUrlParts["urltoken"]);

        // Compute a new hash
        var computedToken = GenerateUrlToken(controllerName, actionName, requestUrlParts, password);

        // compare with the submited hash
        if (computedToken != token)
        {
            computedToken = GenerateUrlToken(string.Empty, actionName, requestUrlParts, password);
        }
        else
        {
            return true;
        }

        return computedToken == token;
    }

    /// <summary>
    /// This method create the random dynamic key for the session
    /// </summary>
    /// <returns></returns>
    private static string RandomString()
    {
        var builder = new StringBuilder();
        var random = new Random();
        for (var i = 0; i < 8; i++)
        {
            var ch = Convert.ToChar(Convert.ToInt32(Math.Floor((26 * random.NextDouble()) + 65)));
            builder.Append(ch);
        }

        return builder.ToString();
    }
}

 

ExtendedHtmlActionLinkHelper (3).zip (277.13 kb)

Original Source

File Upload does not check content

Is it possible to upload files of any type to the server through web application’s functionality?

Only file extension restrictions were could be enforced and the server may not perform any content checking (apart from possible anti-virus checks).

At the very least, a rudimentary check of the file content/headers must be made to ensure that the file extension does relate to the file content and that both are acceptable for uploading into the system.

Ideally the file content must be checked to ensure no malicious scripts or macros in word documents are present.

Recommendation:

All uploaded files should be thoroughly scanned and validated before being made available to other users.

If in doubt, the file should be discarded. Ideally, only files of certain types should be allowed and checking for file types should be done by the server for their content and file extensions, Content-Encoding or Content-Type headers should not be relied upon.

I’ve produced a simple application that checks for file signature when upload that can be used

http://bryanavery.co.uk/post/2013/06/25/Determine-which-file-type-has-been-uploaded/

Anothe rsource of reference can he found here:

http://stackoverflow.com/questions/58510/using-net-how-can-you-find-the-mime-type-of-a-file-based-on-the-file-signature

SSL Vulnerable to BEAST attack

A vulnerability exists in SSL 3.0 and TLS 1.0 that could allow information disclosure if an attacker intercepts encrypted traffic served from an affected system. The weakness is down to insufficiently randomised data being used for the Initialisation Vectors (IV) within the CBC-mode encryption algorithms. The exploit can be performed through multiple injection points, both native to the browser’s functionality.

This issue is reduced in risk by the fact that the attacker is required to have a Man-in-the-Middle position for this exploit whereby traffic interception can be performed, and with this position obtained it is generally easier to attack the victim through other methods (SSL-stripping, mixed-scripting [requesting HTTP resources from an HTTPS connection], etc…) which do not require complex cryptanalysis such as BEAST to execute.

TestSSLServer.exe (43.00 kb)

http://www.bolet.org/TestSSLServer/

 

Supported versions:
 SSLv2 SSLv3 TLSv1.0
Deflate compression: no
Supported cipher suites (ORDER IS NOT SIGNIFICANT):
  SSLv2
     RC4_128_WITH_MD5
     DES_192_EDE3_CBC_WITH_MD5
  SSLv3
     RSA_WITH_RC4_128_MD5
     RSA_WITH_RC4_128_SHA
     RSA_WITH_3DES_EDE_CBC_SHA
  TLSv1.0
     RSA_WITH_RC4_128_MD5
     RSA_WITH_RC4_128_SHA
     RSA_WITH_3DES_EDE_CBC_SHA
     RSA_WITH_AES_128_CBC_SHA
     RSA_WITH_AES_256_CBC_SHA
     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
     TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
----------------------
Server certificate(s):
  0edc8b5e2d1e4c803319c3e4e80dd9945d953db2: CN=application.local.someone.zone
----------------------
Minimal encryption strength:     strong encryption (96-bit or more)
Achievable encryption strength:  strong encryption (96-bit or more)
BEAST status: vulnerable
CRIME status: protected

Recommendation:

Client-side:

Various browsers are introducing or have introduced mitigations for the issue which make exploitation less likely. There are also steps which can be taken on the server side to make exploitation impossible.

Server-side:

Enabling and prioritising TLS 1.1/1.2 would be advised where possible although removing support for TLS1.0 is impractical at this time.

In the short-term due to the lack of wide-scale support by browsers and servers alike, prioritising the use of a stream cipher (such as RC4-SHA) instead of a CBC-mode cipher is recommended in order to maintain compatibility with browsers (see note).

Migration away from TLS 1.0 and below to TLS 1.1/1.2 should considered as a medium-term option for secure applications.

SSL Best Practice Guide:

https://www.ssllabs.com/projects/best-practices/index.html

BEAST attack

On September 23, 2011 researchers Thai Duong and Juliano Rizzo demonstrated a “proof of concept” called BEAST (“Browser Exploit Against SSL/TLS”) using a Java applet to violate same origin policy constraints, for a long-known Cipher block chaining (CBC) vulnerability in TLS 1.0.[44][45] Practical exploits had not been previously demonstrated for this vulnerability, which was originally discovered by Phillip Rogaway[46] in 2002. The vulnerability of the attack had been fixed with TLS 1.1 in 2006, but TLS 1.1 had not seen wide adoption prior to this attack demonstration.

Mozilla updated the development versions of their NSS libraries to mitigate BEAST-like attacks. NSS is used by Mozilla Firefox and Google Chrome to implement SSL. Some web servers that have a broken implementation of the SSL specification may stop working as a result.

Microsoft released Security Bulletin MS12-006 on January 10, 2012, which fixed the BEAST vulnerability by changing the way that the Windows Secure Channel (SChannel) component transmits encrypted network packets.

Users of Windows 7 and Windows Server 2008 R2 can enable use of TLS 1.1 and 1.2, but this work-around will fail if it is not supported by the other end of the connection and will result in a fall-back to TLS 1.0.

Verbose Error Messages

The software generates an error message that includes sensitive information about its environment, users, or associated data. 

Extended Description

The sensitive information may be valuable information on its own (such as a password), or it may be useful for launching other, more deadly attacks. If an attack fails, an attacker may use error information provided by the server to launch another more focused attack. For example, an attempt to exploit a path traversal weakness might yield the full pathname of the installed application. In turn, this could be used to select the proper number of “..” sequences to navigate to the targeted file. An attack using SQL injection might not initially succeed, but an error message could reveal the malformed query, which would expose query logic and possibly even passwords or other sensitive information used within the query.

Recommendation

Suppress these error messages by ensuring that a customised error handler is called in the event of an error. This can produce a generic message which does not hint at the underlying cause of the exception.

References

OWASP – Error Handling
http://www.owasp.org/index.php/Improper_Error_Handling

How to: Display Safe Error Messages

When your application displays error messages, it should not give away information that a malicious user might find helpful in attacking your system. For example, if your application unsuccessfully tries to log in to a database, it should not display an error message that includes the user name it is using.

There are a number of ways to control error messages, including the following:

Configure the application not to show verbose error messages to remote users. (Remote users are those who request pages while not working on the Web server computer.) You can optionally redirect errors to an application page.

Include error handling whenever practical and construct your own error messages. In your error handler, you can test to see whether the user is local and react accordingly.

Create a global error handler at the page or application level that catches all unhandled exceptions and routes them to a generic error page. That way, even if you did not anticipate a problem, at least users will not see an exception page.

To configure the application to turn off errors for remote users

In the Web.config file for your application, make the following changes to the customErrors element:

  • Set the mode attribute to RemoteOnly (case-sensitive). This configures the application to show detailed errors only to local users (that is, to you, the developer).
  • Optionally include a defaultRedirect attribute that points to an application error page.
  • Optionally include <error> elements that redirect specific errors to specific pages. For example, you can redirect standard 404 errors (page not found) to your own application page.

The following code example shows a typical customErrors block in the Web.config file.

<customErrors mode="RemoteOnly" defaultRedirect="AppErrors/">
   <error statusCode="404" redirect="NoSuchPage/"/>
   <error statusCode="403" redirect="NoAccessAllowed/"/>
</customErrors>

Environment issue – Untrusted Certificate

Certificate is singed by an unrecognised certificate authority.  If a browser receives a self-signed certificate, it pops up a warning, and the burden falls to the user to confirm the identity.  Pushing this decision to the user is ultimately what opens up the possibility of a man-in-the-middle (MITM) attack.  The security issue is not with self-signed certificates, but with the way users interact with them in the browser.

Recommendation:

Purchase or generate a proper certificate for this service.

Check out, for Mand in the middle

http://www.schneier.com/blog/archives/2010/04/man-in-the-midd_2.html

You can purchase SSL certificates from Symantec

https://www.symantec.com/en/uk/verisign/ssl-certificates/secure-site

Denial Of Service (DoS) attacks via SQL Wildcards should be prevented

SQL Wildcard attacks force the underlying database to carry out CPU-intensive queries by using several wildcards. This vulnerability generally exists in search functionalities of web applications. Successful exploitation of this attack will cause Denial of Service (DoS).

Depending on the connection pooling settings of the application and the time taken for attack query to execute, an attacker might be able to consume all connections in the connection pool, which will cause database queries to fail for legitimate users.

By default in ASP.NET, the maximum allowed connections in the pool is 100 and timeout is 30 seconds. Thus if an attacker can run 100 multiple queries with 30+ seconds execution time within 30 seconds no one else would be able to use the database related parts of the application.

Recommendation

If the application does not require this sort of advanced search, all wildcards should be escaped or filtered.

References:

OWASP Testing for SQL Wildcard Attacks

https://www.owasp.org/index.php/Testing_for_SQL_Wildcard_Attacks_(OWASP-DS-001)

DoS Attacks using SQL Wildcards

http://www.zdnet.com/blog/security/dos-attacks-using-sql-wildcards-revealed/1134

Brief Summary

SQL Wildcard Attacks are about forcing the underlying database to carry out CPU-intensive queries by using several wildcards. This vulnerability generally exists in search functionalities of web applications. Successful exploitation of this attack will cause Denial of Service.

Description of the Issue

SQL Wildcard attacks might affect all database back-ends but mainly affect SQL Server because the MS SQL Server LIKE operator supports extra wildcards such as “[]”,”[^]”,”_” and “%”.

In a typical web application, if you were to enter “foo” into the search box, the resulting SQL query might be:

SELECT * FROM Article WHERE Content LIKE ‘%foo%’

In a decent database with 1-100000 records the query above will take less than a second. The following query, in the very same database, will take about 6 seconds with only 2600 records.

SELECT TOP 10 * FROM Article WHERE Content LIKE ‘%_[^!_%/%a?F%_D)_(F%)_%([)({}%){()}£$&N%_)$*£()$*R”_)][%](%[x])%a][$*”£$-9]_%’

So, if the tester wanted to tie up the CPU for 6 seconds they would enter the following to the search box:

_[^!_%/%a?F%_D)_(F%)_%([)({}%){()}£$&N%_)$*£()$*R”_)][%](%[x])%a][$*”£$-9]_

Black Box testing and example

Testing for SQL Wildcard Attacks:

Craft a query which will not return a result and includes several wildcards. You can use one of the example inputs below.

Send this data through the search feature of the application. If the application takes more time generating the result set than a usual search would take, it is vulnerable.

Example Attack Inputs to send

    ‘%_[^!_%/%a?F%_D)_(F%)_%([)({}%){()}£$&N%_)$*£()$*R”_)][%](%[x])%a][$*”£$-9]_%’
    ‘%64_[^!_%65/%aa?F%64_D)_(F%64)_%36([)({}%33){()}£$&N%55_)$*£()$*R”_)][%55](%66[x])%ba][$*”£$-9]_%54’ bypasses modsecurity
    _[r/a)_ _(r/b)_ _(r-d)_
    %n[^n]y[^j]l[^k]d[^l]h[^z]t[^k]b[^q]t[^q][^n]!%
    %_[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[! -z]@$!_%

Result Expected

If the application is vulnerable, the response time should be longer than usual.

SQL Wildcard attacks force the underlying database to carry out CPU-intensive queries by using several wildcards. This vulnerability generally exists in search functionalities of web applications. Successful exploitation of this attack will cause Denial of Service.

Here, it was possible to search by keying in a value for the forename/firstname field as M__E (i.e. M underscore underscore E) and the search returned results treating the two underscores as wildcards for any two characters.

This may still exist on the current code base so it’s a case of going through the forms and ensuring that it doesn’t occur / fix it if the vulnerability exists.

DoS Attacks Using SQL Wiltcards.pdf (567.23 kb)

Check the solution I have produced to prevent this Removing and Cleaning search content to prevent DoS attacks

HTTP Header Disclosure

Vulnerability overview/description

Due to unsanitized user input it is possible to inject arbitrary HTTP header values in certain HTTP responses of the Satellite Server. This can be exploited, for example, to perform session fixation and malicious redirection attacks via the Set-Cookie and the Refresh headers. Moreover, the Satellite Server caches these HTTP responses with the injected HTTP header resulting in all further requests to the same resource being served with the poisoned HTTP response, while these objects remain in cache.

Information Disclosure

Information disclosure enables an attacker to gain valuable information about a system. Therefore, always consider what information you are revealing and whether it can be used by a malicious user. The following lists possible information disclosure attacks and provides mitigations for each. 

Message Security and HTTP

If you are using message-level security over an HTTP transport layer, be aware that message-level security does not protect HTTP headers. The only way to protect HTTP headers is to use HTTPS transport instead of HTTP. HTTPS transport causes the entire message, including the HTTP headers, to be encrypted using the Secure Sockets Layer (SSL) protocol.

http://msdn.microsoft.com/en-us/library/aa738441/ 

Environment change

The web application returned information about itself in the HTTP header that could aid an attacker.  Default web server installations often include the vendor and version details of the web application, and possibly further information about scripting services also installed