CVE-2015-8814 (C#, published 2017-03-03T16:59Z)
Description: value: Umbraco before 7.4.0 allows remote attackers to bypass anti-forgery security measures and conduct cross-site request forgery (CSRF) attacks as demonstrated by editing user account information in the templates.asmx.cs file.
Severity: MEDIUM (CVSS v3 base score: 8.8)
Weakness: CWE-352 \u2014 Cross-Site Request Forgery (CSRF)
The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
Repository: https://github.com/umbraco/Umbraco-CMS
Files changed:
src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js
src/Umbraco.Web/Mvc/ValidateMvcAngularAntiForgeryTokenAttribute.cs
src/Umbraco.Web/Umbraco.Web.csproj
src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs
src/Umbraco.Web/WebServices/BulkPublishController.cs
src/Umbraco.Web/WebServices/DomainsApiController.cs
src/Umbraco.Web/WebServices/ExamineManagementApiController.cs
src/Umbraco.Web/WebServices/SaveFileController.cs
src/Umbraco.Web/WebServices/XmlDataIntegrityController.cs
Commit message: Fixes U4-7459 XSRF protection bypass  - ensures tokens are checked for the non-editor api controllers


Vulnerable code:
        /// <summary>
        /// Validates the headers/cookies passed in for the request
        /// </summary>
        /// <param name="requestHeaders"></param>
        /// <param name="failedReason"></param>
        /// <returns></returns>
        public static bool ValidateHeaders(HttpRequestHeaders requestHeaders, out string failedReason)

            var cookieToken = requestHeaders
                .GetCookies()
                .Select(c => c[CsrfValidationCookieName])
                .FirstOrDefault();

            if (ValidateTokens(cookieToken.Value, headerToken) == false)
            


Fixed code:
    //This sets the default jquery ajax headers to include our csrf token, we
    // need to user the beforeSend method because our token changes per user/login so
    // it cannot be static
    $.ajaxSetup({
        beforeSend: function (xhr) {

            function getCookie(name) {
                var value = "; " + document.cookie;
                var parts = value.split("; " + name + "=");
                if (parts.length === 2) return parts.pop().split(";").shift();
            }

            xhr.setRequestHeader("X-XSRF-TOKEN", getCookie("XSRF-TOKEN"));
        }
    });

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Web.Mvc;
using Umbraco.Web.WebApi.Filters;

namespace Umbraco.Web.Mvc
{
    /// <summary>
    /// A filter to check for the csrf token based on Angular's standard approach
    /// </summary>
    /// <remarks>
    /// Code derived from http://ericpanorel.net/2013/07/28/spa-authentication-and-csrf-mvc4-antiforgery-implementation/
    /// 
    /// If the authentication type is cookie based, then this filter will execute, otherwise it will be disabled
    /// </remarks>
    public sealed class ValidateMvcAngularAntiForgeryTokenAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var userIdentity = filterContext.HttpContext.User.Identity as ClaimsIdentity;
            if (userIdentity != null)
            {
                //if there is not CookiePath claim, then exist
                if (userIdentity.HasClaim(x => x.Type == ClaimTypes.CookiePath) == false)
                {
                    base.OnActionExecuting(filterContext);
                    return;
                }
            }

            string failedReason;
            var headers = new List<KeyValuePair<string, List<string>>>();
            foreach (var key in filterContext.HttpContext.Request.Headers.AllKeys)
            {
                if (headers.Any(x => x.Key == key))
                {
                    var found = headers.First(x => x.Key == key);
                    found.Value.Add(filterContext.HttpContext.Request.Headers[key]);
                }
                else
                {
                    headers.Add(new KeyValuePair<string, List<string>>(key, new List<string> { filterContext.HttpContext.Request.Headers[key] }));
                }
            }
            var cookie = filterContext.HttpContext.Request.Cookies[AngularAntiForgeryHelper.CsrfValidationCookieName];
            if (AngularAntiForgeryHelper.ValidateHeaders(
                headers.Select(x => new KeyValuePair<string, IEnumerable<string>>(x.Key, x.Value)).ToArray(),
                cookie == null ? "" : cookie.Value,
                out failedReason) == false)
            {
                var result = new HttpStatusCodeResult(HttpStatusCode.ExpectationFailed);
                filterContext.Result = result;
                return;
            }

            base.OnActionExecuting(filterContext);
        }
    }
}
    <Compile Include="Mvc\ValidateMvcAngularAntiForgeryTokenAttribute.cs" />
using System.Collections.Generic;
using System.Collections.Specialized;
        

        internal static bool ValidateHeaders(            
            KeyValuePair<string, IEnumerable<string>>[] requestHeaders, 
            string cookieToken,
            out string failedReason)
            
            if (ValidateTokens(cookieToken, headerToken) == false)


        /// <summary>
        /// Validates the headers/cookies passed in for the request
        /// </summary>
        /// <param name="requestHeaders"></param>
        /// <param name="failedReason"></param>
        /// <returns></returns>
        public static bool ValidateHeaders(HttpRequestHeaders requestHeaders, out string failedReason)
        {
            var cookieToken = requestHeaders
                .GetCookies()
                .Select(c => c[CsrfValidationCookieName])
                .FirstOrDefault();

            return ValidateHeaders(
                requestHeaders.ToDictionary(x => x.Key, x => x.Value).ToArray(),
                cookieToken == null ? null : cookieToken.Value,
                out failedReason);
        }
    [ValidateMvcAngularAntiForgeryToken]
using Umbraco.Web.WebApi.Filters;
    [ValidateAngularAntiForgeryToken]
using Umbraco.Web.WebApi.Filters;
    [ValidateAngularAntiForgeryToken]
    [ValidateMvcAngularAntiForgeryToken]
using Umbraco.Web.WebApi.Filters;
    [ValidateAngularAntiForgeryToken]