New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft Security Advisory CVE-2018-0784 - ASP.NET Core Templates enable Elevation Of Privilege Vulnerability #285

Open
blowdart opened this Issue Jan 9, 2018 · 0 comments

Comments

Projects
None yet
2 participants
@blowdart
Member

blowdart commented Jan 9, 2018

Microsoft Security Advisory CVE-2018-0784

ASP.NET Core Templates enable Elevation Of Privilege Vulnerability

Executive Summary

Microsoft is releasing this security advisory to provide information about a vulnerability in the public versions of ASP.NET Core 2.0. This advisory also provides guidance on what developers can do to update their applications correctly.

Microsoft is aware of an elevation of privilege vulnerability exists when a ASP.NET Core web application, created using vulnerable project templates, fails to properly sanitize web requests. An attacker who successfully exploited this vulnerability could perform content injection attacks and run script in the security context of the logged-on user.

To exploit the vulnerability, an attacker could send a specially crafted email, containing a malicious link, to a user. Alternatively, an attacker could use a chat client to social engineer a user into clicking the malicious link. However, in all cases to exploit this vulnerability a user must click a maliciously crafted link from an attacker.

The security update addresses the vulnerability by correcting the ASP.NET Core project templates.

Developers who have generated applications from the vulnerable templates should change their code using the following instructions. They should also change their code to address a further vulnerability, CVE-2018-0785, which is in the same templates. Finally installing the latest .NET Core SDK, version 2.1.4, from https://www.microsoft.com/net/download/ will update the templates to correct the issue for any new applications created.

Discussion

Please use aspnet/Templating#225 for discussion of this advisory.

Mitigation Factors

ASP.NET Core applications which are not created using the ASP.NET Core 2.0 Individual Authentication templates are not vulnerable to this issue.

Affected Software

The vulnerabilities affect any Microsoft .NET Core project if it uses any of affected runtime versions listed below and have generated applications using Individual Authentication with usernames and passwords stored within the application. Applications which use Azure Active Directory, or Azure Active Directory B2C are not affected.

Vulnerable .NET SDK Version Fixed SDK Version
2.0.0, 2.0.2, 2.0.3, 2.1.2, 2.1.3 2.1.4

Advisory FAQ

How do I know if I am affected?

Your application will be affected if you generated it using the ASP.NET 2.0 Web Application template or the ASP.NET 2.0 Web Application (Model/View/Controller) template from a vulnerable SDK version where you have selected individual authentication where user accounts are stored in-app.

To check the runtimes installed on a computer you must view the contents of the runtime folder. By default these are

Operating System Location
Windows C:\Program Files\dotnet\sdk\
macOS /usr/local/share/dotnet/sdk/
Supported Linux platforms /usr/share/dotnet/sdk/

Each SDK version is installed in its own directory, where the directory name is the version number. If you do not have a directory for 2.1.4 then any applications generated for ASP.NET Core 2.0 using Individual Authentication may be vulnerable.. Downloads for all supported platforms can be acquired from https://www.microsoft.com/net/download/

How do I fix my affected application?

Applications can be fixed by changing the code created during application generation using the following instructions.

For ASP.NET Core 2.0 Web Application (Razor Pages)

  1. Open Pages\Account\Manage\EnableAuthenticator.cshtml.cs find the OnPostAsync() method and search for the following line
<div id="qrCodeData" data-url="@Html.Raw(Model.AuthenticatorUri)"></div>

Replace this line with the following code, removing the call to Html.Raw()

<div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div>
  1. Open the Pages\Account\Manage\EnableAuthenticator.cshtml.cs and search for the following line, inside the OnGetAsync() method;
await LoadSharedKeyAndQrCodeUriAsync(user);

Remove the code shown below which follows the call to LoadSharedKeyAndQrCodeUriAsync(user);

if (string.IsNullOrEmpty(SharedKey))		
{		
    await _userManager.ResetAuthenticatorKeyAsync(user);		
    await LoadSharedKeyAndQrCodeUriAsync(user);		
}

The OnGetAsync() method should now look like

public async Task<IActionResult> OnGetAsync()
{
    var user = await _userManager.GetUserAsync(User);
    if (user == null)
    {
        throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
    }

    await LoadSharedKeyAndQrCodeUriAsync(user);

    return Page();
}
  1. Still in the Pages\Account\Manage\EnableAuthenticator.cshtml.cs file and search for the LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user) function and replace it with
private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user)
{
    // Load the authenticator key & QR code URI to display on the form
    var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
    if (string.IsNullOrEmpty(unformattedKey))
    {
        await _userManager.ResetAuthenticatorKeyAsync(user);
        unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
     }

    SharedKey = FormatKey(unformattedKey);
    AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey);
 }
  1. Recompile your application and test you can generate 2fa recovery codes correctly, then redeploy your application.

For ASP.NET Core 2.0 Web Application (Model/View/Controller)

  1. Open the Controllers\ManageController.cs file and find the declaration for private const string AuthenticatorUriFormat. Add the following new const declaration below it;
private const string RecoveryCodesKey = nameof(RecoveryCodesKey);
  1. Still in Controllers\ManageController.cs file and find the EnableAuthenticator() function. Replace its contents with
[HttpGet]
public async Task<IActionResult> EnableAuthenticator()
{
    var user = await _userManager.GetUserAsync(User);
    if (user == null)
    {
        throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
    }

    var model = new EnableAuthenticatorViewModel();
    await LoadSharedKeyAndQrCodeUriAsync(user, model);

    return View(model);
}
  1. Still in Controllers\ManageController.cs file and find the EnableAuthenticator(EnableAuthenticatorViewModel model) method. Replace its contents with
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EnableAuthenticator(EnableAuthenticatorViewModel model)
{
    var user = await _userManager.GetUserAsync(User);
    if (user == null)
    {
        throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
    }

    if (!ModelState.IsValid)
    {
        await LoadSharedKeyAndQrCodeUriAsync(user, model);
        return View(model);
     }

    // Strip spaces and hypens
    var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);

    var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
        user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);

    if (!is2faTokenValid)
    {
        ModelState.AddModelError("Code", "Verification code is invalid.");
        await LoadSharedKeyAndQrCodeUriAsync(user, model);
        return View(model);
    }

    await _userManager.SetTwoFactorEnabledAsync(user, true);
    _logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id);
    var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
    TempData[RecoveryCodesKey] = recoveryCodes.ToArray();

    return RedirectToAction(nameof(ShowRecoveryCodes));
}
  1. Still in Controllers\ManageController.cs file create the following method after EnableAuthenticator(EnableAuthenticatorViewModel model);
[HttpGet]
public IActionResult ShowRecoveryCodes()
{
    var recoveryCodes = (string[])TempData[RecoveryCodesKey];
    if (recoveryCodes == null)
    {
        return RedirectToAction(nameof(TwoFactorAuthentication));
    }

    var model = new ShowRecoveryCodesViewModel { RecoveryCodes = recoveryCodes };
    return View(model);
}
  1. Still in Controllers\ManageController.cs file create the following method after the GenerateQrCodeUri(string email, string unformattedKey) method
private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user, EnableAuthenticatorViewModel model)
{
    var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
    if (string.IsNullOrEmpty(unformattedKey))
    {
        await _userManager.ResetAuthenticatorKeyAsync(user);
        unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
    }

    model.SharedKey = FormatKey(unformattedKey);
    model.AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey);
}
  1. Still in Controllers\ManageController.cs Create a new action method in the controller file, GenerateRecoveryCodesWarning() containing the following code
[HttpGet]
public async Task<IActionResult> GenerateRecoveryCodesWarning()
{
    var user = await _userManager.GetUserAsync(User);
    if (user == null)
    {
        throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
    }

    if (!user.TwoFactorEnabled)
    {
        throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' because they do not have 2FA enabled.");
    }

    return View(nameof(GenerateRecoveryCodesWarning));
}
  1. Open the Models\ManageViewModels\EnableAuthenticatorViewModel.cs file and add the following using reference;
using Microsoft.AspNetCore.Mvc.ModelBinding;

then replace the class declaration with

public class EnableAuthenticatorViewModel
{
    [Required]
    [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
    [DataType(DataType.Text)]
    [Display(Name = "Verification Code")]
    public string Code { get; set; }

    [BindNever]
    public string SharedKey { get; set; }

    [BindNever]
    public string AuthenticatorUri { get; set; }
}
  1. Open the Views\Manage\EnableAuthenticator.cshtml file and replace the following line
<div id="qrCodeData" data-url="@Html.Raw(Model.AuthenticatorUri)"></div>

with

<div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div>

remove the call to Html.Raw().

  1. Recompile your application and test you can generate 2fa recovery codes correctly, then redeploy your application.

Other Information

Reporting Security Issues

If you have found a potential security issue in .NET Core, please email details to secure@microsoft.com. Reports may qualify for the .NET Core Bug Bounty. Details of the .NET Core Bug Bounty including Terms and Conditions are at https://aka.ms/corebounty.

Support

You can ask questions about this issue on GitHub in the .NET Core or ASP.NET Core organizations. These are located at https://github.com/dotnet/ and https://github.com/aspnet/. The Announcements repo for each product (https://github.com/dotnet/Announcements and https://github.com/aspnet/Announcements) will contain this bulletin as an issue and will include a link to a discussion issue where you can ask questions.

Acknowledgments

Thanks to Kévin Chalet for reporting this issue.

External Links

CVE-2018-0784

Revisions

V1.0 (Jan 9, 2018): Corrected text in link to CVE-2018-0785.
V1.0 (Jan 9, 2018): Advisory published.

Version 1.1
Last Updated 2018-01-10

@blowdart blowdart added the Security label Jan 9, 2018

@aspnet aspnet locked and limited conversation to collaborators Jan 9, 2018

@Eilon Eilon added this to the 2.0.5 milestone Jan 9, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.