VYPR
Moderate severityNVD Advisory· Published Jun 24, 2025· Updated Jun 24, 2025

Umbraco.Cms Vulnerable to Disclosure of Configured Password Requirements

CVE-2025-49147

Description

Umbraco, a free and open source .NET content management system, has a vulnerability in versions 10.0.0 through 10.8.10 and 13.0.0 through 13.9.1. Via a request to an anonymously authenticated endpoint it's possible to retrieve information about the configured password requirements. The information available is limited but would perhaps give some additional detail useful for someone attempting to brute force derive a user's password. This information was not exposed in Umbraco 7 or 8, nor in 14 or higher versions. The vulnerability is patched in versions 10.8.11 and 13.9.2.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
Umbraco.CmsNuGet
>= 10.0.0, < 10.8.1110.8.11
Umbraco.CmsNuGet
>= 13.0.0, < 13.9.213.9.2

Affected products

1

Patches

2
b4144564c836

Merge commit from fork

https://github.com/umbraco/Umbraco-CMSKenn JacobsenJun 24, 2025via ghsa
3 files changed · +31 4
  • src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs+15 4 modified
    @@ -131,12 +131,17 @@ public AuthenticationController(
             AuthorizationPolicies.BackOfficeAccess)] // Needed to enforce the principle set on the request, if one exists.
         public IDictionary<string, object> GetPasswordConfig(int userId)
         {
    +        if (HttpContext.HasActivePasswordResetFlowSession(userId))
    +        {
    +            return _passwordConfiguration.GetConfiguration();
    +        }
    +
             Attempt<int> currentUserId =
                 _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId() ?? Attempt<int>.Fail();
    -        return _passwordConfiguration.GetConfiguration(
    -            currentUserId.Success
    -                ? currentUserId.Result != userId
    -                : true);
    +
    +        return currentUserId.Success
    +            ? _passwordConfiguration.GetConfiguration(currentUserId.Result != userId)
    +            : new Dictionary<string, object>();
         }
     
         /// <summary>
    @@ -417,6 +422,8 @@ public async Task<bool> IsAuthenticated()
         [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
         public async Task<ActionResult<UserDetail?>> PostLogin(LoginModel loginModel)
         {
    +        HttpContext.EndPasswordResetFlowSession();
    +
             // Start a timed scope to ensure failed responses return is a consistent time
             var loginDuration = Math.Max(_loginDurationAverage ?? _securitySettings.UserDefaultFailedLoginDurationInMilliseconds, _securitySettings.UserMinimumFailedLoginDurationInMilliseconds);
             await using var timedScope = new TimedScope(loginDuration, HttpContext.RequestAborted);
    @@ -490,6 +497,8 @@ public async Task<IActionResult> PostRequestPasswordReset(RequestPasswordResetMo
                 return BadRequest();
             }
     
    +        HttpContext.EndPasswordResetFlowSession();
    +
             BackOfficeIdentityUser? identityUser = await _userManager.FindByEmailAsync(model.Email);
     
             await Task.Delay(RandomNumberGenerator.GetInt32(400, 2500)); // To randomize response time preventing user enumeration
    @@ -646,6 +655,8 @@ public async Task<IActionResult> PostSend2FACode([FromBody] string provider)
         [AllowAnonymous]
         public async Task<IActionResult> PostSetPassword(SetPasswordModel model)
         {
    +        HttpContext.EndPasswordResetFlowSession();
    +
             BackOfficeIdentityUser? identityUser =
                 await _userManager.FindByIdAsync(model.UserId.ToString(CultureInfo.InvariantCulture));
                 if (identityUser is null)
    
  • src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs+5 0 modified
    @@ -402,6 +402,11 @@ public async Task<IActionResult> ValidatePasswordResetCode([Bind(Prefix = "u")]
     
             var result = await _userManager.VerifyUserTokenAsync(user, "Default", "ResetPassword", resetCode);
     
    +        if (result)
    +        {
    +            HttpContext.StartPasswordResetFlowSession(userId);
    +        }
    +
             return result ?
     
                 // Redirect to login with userId and resetCode
    
  • src/Umbraco.Web.BackOffice/Extensions/HttpContextExtensions.cs+11 0 modified
    @@ -5,9 +5,20 @@ namespace Umbraco.Extensions;
     
     public static class HttpContextExtensions
     {
    +    private const string PasswordResetFlowSessionKey = nameof(PasswordResetFlowSessionKey);
    +
         public static void SetExternalLoginProviderErrors(this HttpContext httpContext, BackOfficeExternalLoginProviderErrors errors)
             => httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] = errors;
     
         public static BackOfficeExternalLoginProviderErrors? GetExternalLoginProviderErrors(this HttpContext httpContext)
             => httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] as BackOfficeExternalLoginProviderErrors;
    +
    +    internal static void StartPasswordResetFlowSession(this HttpContext httpContext, int userId)
    +        => httpContext.Session.SetInt32(PasswordResetFlowSessionKey, userId);
    +
    +    internal static void EndPasswordResetFlowSession(this HttpContext httpContext)
    +        => httpContext.Session.Remove(PasswordResetFlowSessionKey);
    +
    +    internal static bool HasActivePasswordResetFlowSession(this HttpContext httpContext, int userId)
    +        => httpContext.Session.GetInt32(PasswordResetFlowSessionKey) == userId;
     }
    
d8f68d2c40f8

Merge commit from fork

https://github.com/umbraco/Umbraco-CMSKenn JacobsenJun 24, 2025via ghsa
3 files changed · +28 4
  • src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs+15 4 modified
    @@ -132,12 +132,17 @@ public AuthenticationController(
             AuthorizationPolicies.BackOfficeAccess)] // Needed to enforce the principle set on the request, if one exists.
         public IDictionary<string, object> GetPasswordConfig(int userId)
         {
    +        if (HttpContext.HasActivePasswordResetFlowSession(userId))
    +        {
    +            return _passwordConfiguration.GetConfiguration();
    +        }
    +
             Attempt<int> currentUserId =
                 _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId() ?? Attempt<int>.Fail();
    -        return _passwordConfiguration.GetConfiguration(
    -            currentUserId.Success
    -                ? currentUserId.Result != userId
    -                : true);
    +
    +        return currentUserId.Success
    +            ? _passwordConfiguration.GetConfiguration(currentUserId.Result != userId)
    +            : new Dictionary<string, object>();
         }
     
         /// <summary>
    @@ -345,6 +350,8 @@ public async Task<bool> IsAuthenticated()
         [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
         public async Task<ActionResult<UserDetail?>> PostLogin(LoginModel loginModel)
         {
    +        HttpContext.EndPasswordResetFlowSession();
    +
             // Start a timed scope to ensure failed responses return is a consistent time
             await using var timedScope = new TimedScope(GetLoginDuration(), CancellationToken.None);
     
    @@ -440,6 +447,8 @@ public async Task<IActionResult> PostRequestPasswordReset(RequestPasswordResetMo
                 return BadRequest();
             }
     
    +        HttpContext.EndPasswordResetFlowSession();
    +
             BackOfficeIdentityUser? identityUser = await _userManager.FindByEmailAsync(model.Email);
     
             await Task.Delay(RandomNumberGenerator.GetInt32(400, 2500)); // To randomize response time preventing user enumeration
    @@ -593,6 +602,8 @@ public async Task<IActionResult> PostSend2FACode([FromBody] string provider)
         [AllowAnonymous]
         public async Task<IActionResult> PostSetPassword(SetPasswordModel model)
         {
    +        HttpContext.EndPasswordResetFlowSession();
    +
             BackOfficeIdentityUser? identityUser =
                 await _userManager.FindByIdAsync(model.UserId.ToString(CultureInfo.InvariantCulture));
     
    
  • src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs+2 0 modified
    @@ -370,6 +370,8 @@ public async Task<IActionResult> ValidatePasswordResetCode([Bind(Prefix = "u")]
                 var result = await _userManager.VerifyUserTokenAsync(user, "Default", "ResetPassword", resetCode);
                 if (result)
                 {
    +                HttpContext.StartPasswordResetFlowSession(userId);
    +
                     //Add a flag and redirect for it to be displayed
                     TempData[ViewDataExtensions.TokenPasswordResetCode] =
                         _jsonSerializer.Serialize(
    
  • src/Umbraco.Web.BackOffice/Extensions/HttpContextExtensions.cs+11 0 modified
    @@ -5,9 +5,20 @@ namespace Umbraco.Extensions;
     
     public static class HttpContextExtensions
     {
    +    private const string PasswordResetFlowSessionKey = nameof(PasswordResetFlowSessionKey);
    +
         public static void SetExternalLoginProviderErrors(this HttpContext httpContext, BackOfficeExternalLoginProviderErrors errors)
             => httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] = errors;
     
         public static BackOfficeExternalLoginProviderErrors? GetExternalLoginProviderErrors(this HttpContext httpContext)
             => httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] as BackOfficeExternalLoginProviderErrors;
    +
    +    internal static void StartPasswordResetFlowSession(this HttpContext httpContext, int userId)
    +        => httpContext.Session.SetInt32(PasswordResetFlowSessionKey, userId);
    +
    +    internal static void EndPasswordResetFlowSession(this HttpContext httpContext)
    +        => httpContext.Session.Remove(PasswordResetFlowSessionKey);
    +
    +    internal static bool HasActivePasswordResetFlowSession(this HttpContext httpContext, int userId)
    +        => httpContext.Session.GetInt32(PasswordResetFlowSessionKey) == userId;
     }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.